Compare commits
No commits in common. "c7a86c04ab20563ce1c0876929d54fbc51bd464d" and "eebb93a9a6d9ba96cca071c2d7d34f20d7c47b80" have entirely different histories.
c7a86c04ab
...
eebb93a9a6
|
@ -40,27 +40,6 @@
|
||||||
},
|
},
|
||||||
"args": [],
|
"args": [],
|
||||||
"cwd": "${workspaceFolder}"
|
"cwd": "${workspaceFolder}"
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "lldb",
|
|
||||||
"request": "launch",
|
|
||||||
"name": "Debug a specific unit tests in executable 'lua-ffi'",
|
|
||||||
"cargo": {
|
|
||||||
"args": [
|
|
||||||
"test",
|
|
||||||
"--no-run",
|
|
||||||
"--package=lua-ffi",
|
|
||||||
"userdata::borrow::tests::ud_methods_borrow",
|
|
||||||
"--",
|
|
||||||
"--exact"
|
|
||||||
]/* ,
|
|
||||||
"filter": {
|
|
||||||
"name": "lua-ffi",
|
|
||||||
"kind": "bin"
|
|
||||||
} */
|
|
||||||
},
|
|
||||||
"args": [],
|
|
||||||
"cwd": "${workspaceFolder}"
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
|
@ -1,18 +0,0 @@
|
||||||
|
|
||||||
variables:
|
|
||||||
- &rust_image 'rust:1.76-alpine'
|
|
||||||
|
|
||||||
steps:
|
|
||||||
Build - Debug:
|
|
||||||
image: *rust_image
|
|
||||||
commands:
|
|
||||||
- apt update
|
|
||||||
- apt install libudev-dev lua5.4 liblua5.4-dev -y
|
|
||||||
- cargo build
|
|
||||||
|
|
||||||
Test - Debug:
|
|
||||||
image: *rust_image
|
|
||||||
commands:
|
|
||||||
- apt update
|
|
||||||
- apt install libudev-dev lua5.4 liblua5.4-dev -y
|
|
||||||
- cargo test --all
|
|
|
@ -1,20 +0,0 @@
|
||||||
variables:
|
|
||||||
- &rust_image 'rust:1.76-alpine'
|
|
||||||
|
|
||||||
when:
|
|
||||||
- event: pull_request
|
|
||||||
|
|
||||||
steps:
|
|
||||||
Build - Release:
|
|
||||||
image: *rust_image
|
|
||||||
commands:
|
|
||||||
- apt update
|
|
||||||
- apt install libudev-dev lua5.4 liblua5.4-dev -y
|
|
||||||
- cargo build --release
|
|
||||||
|
|
||||||
Test - Release:
|
|
||||||
image: *rust_image
|
|
||||||
commands:
|
|
||||||
- apt update
|
|
||||||
- apt install libudev-dev lua5.4 liblua5.4-dev -y
|
|
||||||
- cargo test --all --release
|
|
|
@ -18,11 +18,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "elua"
|
name = "cstr"
|
||||||
version = "0.1.0"
|
version = "0.2.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8aa998c33a6d3271e3678950a22134cd7dd27cef86dee1b611b5b14207d1d90b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"mlua-sys",
|
"proc-macro2",
|
||||||
"thiserror",
|
"quote",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -31,6 +33,15 @@ version = "0.2.152"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7"
|
checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lua-ffi"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"cstr",
|
||||||
|
"mlua-sys",
|
||||||
|
"thiserror",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mlua-sys"
|
name = "mlua-sys"
|
||||||
version = "0.5.0"
|
version = "0.5.0"
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
[package]
|
[package]
|
||||||
name = "elua"
|
name = "lua-ffi"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
cstr = "0.2.11"
|
||||||
mlua-sys = { version = "0.5.0", features = ["lua54"] }
|
mlua-sys = { version = "0.5.0", features = ["lua54"] }
|
||||||
thiserror = "1.0.56"
|
thiserror = "1.0.56"
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
# elua
|
|
||||||
The `elua` crate is meant to provide high-level bindings to [Lua 5.4](https://www.lua.org/manual/5.4/manual.html).
|
|
||||||
Although most of the crate is safe, there can be some unsafe aspects. Anytime something
|
|
||||||
could be unsafe, they are marked as so clearly in documentation.
|
|
||||||
|
|
||||||
## State
|
|
||||||
This library is still early in development. Although it provides bindings to most things needed
|
|
||||||
for embedding Lua into your Rust application, there has not been enough testing done to show that
|
|
||||||
it is stable. There also may be things missing, if so, please create an issue!
|
|
|
@ -1,6 +1,6 @@
|
||||||
use std::borrow::{Borrow, Cow};
|
use std::borrow::{Borrow, Cow};
|
||||||
|
|
||||||
use crate::{FromLua, Function, IntoLuaVec, State};
|
use crate::{AsLua, FromLua, Function, State};
|
||||||
|
|
||||||
pub struct Chunk<'a> {
|
pub struct Chunk<'a> {
|
||||||
state: &'a State,
|
state: &'a State,
|
||||||
|
@ -38,7 +38,7 @@ impl<'a> Chunk<'a> {
|
||||||
/// Execute the chunk in the Lua context
|
/// Execute the chunk in the Lua context
|
||||||
pub fn execute<A, R>(&'a self, args: A) -> crate::Result<R>
|
pub fn execute<A, R>(&'a self, args: A) -> crate::Result<R>
|
||||||
where
|
where
|
||||||
A: IntoLuaVec<'a>,
|
A: AsLua<'a>,
|
||||||
R: FromLua<'a>
|
R: FromLua<'a>
|
||||||
{
|
{
|
||||||
self.state.execute_chunk::<A, R>(self, args)
|
self.state.execute_chunk::<A, R>(self, args)
|
||||||
|
|
73
src/error.rs
73
src/error.rs
|
@ -1,73 +0,0 @@
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use mlua_sys as lua;
|
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
|
||||||
pub enum Error {
|
|
||||||
#[error("Syntax error: {0}")]
|
|
||||||
Syntax(String),
|
|
||||||
/// An error returned from lua
|
|
||||||
#[error("Lua runtime error: {0}")]
|
|
||||||
Runtime(String),
|
|
||||||
#[error("Error when running a __gc metamethod")]
|
|
||||||
UserdataGc(String),
|
|
||||||
/// Ran into a not enough memory error when trying to grow the lua stack.
|
|
||||||
#[error("Failed to allocate memory")]
|
|
||||||
MemoryAlloc,
|
|
||||||
#[error("Ran into a nill value on the stack")]
|
|
||||||
Nil,
|
|
||||||
#[error("Unexpected type, expected {0} but got {1}")]
|
|
||||||
UnexpectedType(String, String),
|
|
||||||
#[error("bad argument #{arg_index}{} to `{}` ({error})",
|
|
||||||
.arg_name.clone().map(|a| format!(" (name: {})", a)).unwrap_or("".to_string()),
|
|
||||||
.func.clone().unwrap_or("Unknown".to_string())
|
|
||||||
)]
|
|
||||||
BadArgument {
|
|
||||||
func: Option<String>,
|
|
||||||
arg_index: i32,
|
|
||||||
arg_name: Option<String>,
|
|
||||||
/// the error that describes what was wrong for this argument
|
|
||||||
#[source]
|
|
||||||
error: Arc<Error>
|
|
||||||
},
|
|
||||||
#[error("Incorrect number of arguments, expected {arg_expected}, got {arg_count}")]
|
|
||||||
IncorrectArgCount {
|
|
||||||
arg_expected: i32,
|
|
||||||
arg_count: i32,
|
|
||||||
},
|
|
||||||
#[error("There is already a registry entry with the key {0}")]
|
|
||||||
RegistryConflict(String),
|
|
||||||
#[error("Userdata types did not match")]
|
|
||||||
UserdataMismatch,
|
|
||||||
#[error("Missing meta table for userdata")]
|
|
||||||
MissingMetatable,
|
|
||||||
#[error("An error occurred when attempting to convert from a ValueVec at value index {value_idx}, cause: {error}")]
|
|
||||||
ValueVecError {
|
|
||||||
value_idx: i32,
|
|
||||||
#[source]
|
|
||||||
error: Arc<Error>,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Error {
|
|
||||||
pub fn runtime(msg: &str) -> Self {
|
|
||||||
Self::Runtime(msg.to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn unexpected_type(expected: &str, got: &str) -> Self {
|
|
||||||
Self::UnexpectedType(expected.to_string(), got.to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Throw the error in lua.
|
|
||||||
///
|
|
||||||
/// This method never returns
|
|
||||||
pub unsafe fn throw_lua(self, lua: *mut lua::lua_State) -> ! {
|
|
||||||
let msg = format!("{}\0", self);
|
|
||||||
let msg_c = msg.as_ptr() as *const i8;
|
|
||||||
lua::luaL_error(lua, msg_c);
|
|
||||||
unreachable!();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A result for use with lua functions
|
|
||||||
pub type Result<T> = core::result::Result<T, Error>;
|
|
|
@ -1,6 +1,6 @@
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::{AsLua, FromLua, FromLuaStack, IntoLuaVec, LuaRef, PushToLuaStack, StackGuard, State, Value, ValueVec};
|
use crate::{AsLua, FromLua, FromLuaStack, LuaRef, PushToLuaStack, StackGuard, State, Value, ValueVec};
|
||||||
|
|
||||||
use mlua_sys as lua;
|
use mlua_sys as lua;
|
||||||
|
|
||||||
|
@ -38,15 +38,19 @@ impl<'a> Function<'a> {
|
||||||
|
|
||||||
pub fn exec<A, R>(&self, args: A) -> crate::Result<R>
|
pub fn exec<A, R>(&self, args: A) -> crate::Result<R>
|
||||||
where
|
where
|
||||||
A: IntoLuaVec<'a>,
|
A: AsLua<'a>,
|
||||||
R: FromLua<'a>,
|
R: FromLua<'a>,
|
||||||
{
|
{
|
||||||
unsafe {
|
unsafe {
|
||||||
let _g = StackGuard::new(self.state);
|
let _g = StackGuard::new(self.state);
|
||||||
let s = self.state.state_ptr();
|
let s = self.state.state_ptr();
|
||||||
|
|
||||||
let args_val = args.into_lua_value_vec(self.state)?;
|
let args_val = args.as_lua(self.state)?;
|
||||||
let args_len = args_val.len() as _;
|
let args_len = match args_val {
|
||||||
|
Value::Variable(v) => v.len(),
|
||||||
|
Value::None => 0,
|
||||||
|
_ => 1,
|
||||||
|
} as _;
|
||||||
|
|
||||||
self.state.ensure_stack(2 + args_len)?;
|
self.state.ensure_stack(2 + args_len)?;
|
||||||
|
|
||||||
|
@ -59,6 +63,8 @@ impl<'a> Function<'a> {
|
||||||
};
|
};
|
||||||
|
|
||||||
self.push_to_lua_stack(self.state)?;
|
self.push_to_lua_stack(self.state)?;
|
||||||
|
|
||||||
|
let args_val = args.as_lua(self.state)?;
|
||||||
args_val.push_to_lua_stack(self.state)?;
|
args_val.push_to_lua_stack(self.state)?;
|
||||||
|
|
||||||
match lua::lua_pcall(s, args_len, lua::LUA_MULTRET, handler_idx) {
|
match lua::lua_pcall(s, args_len, lua::LUA_MULTRET, handler_idx) {
|
||||||
|
@ -81,10 +87,8 @@ impl<'a> Function<'a> {
|
||||||
let val = if ret_count > 1 {
|
let val = if ret_count > 1 {
|
||||||
let vals = ValueVec::from_lua_stack(self.state)?;
|
let vals = ValueVec::from_lua_stack(self.state)?;
|
||||||
Value::Variable(vals)
|
Value::Variable(vals)
|
||||||
} else if ret_count == 1 {
|
|
||||||
Value::from_lua_stack(self.state)?
|
|
||||||
} else {
|
} else {
|
||||||
Value::None
|
Value::from_lua_stack(self.state)?
|
||||||
};
|
};
|
||||||
|
|
||||||
R::from_lua(self.state, val)
|
R::from_lua(self.state, val)
|
||||||
|
@ -96,10 +100,4 @@ impl<'a> AsLua<'a> for Function<'a> {
|
||||||
fn as_lua(&self, _lua: &'a State) -> crate::Result<crate::Value<'a>> {
|
fn as_lua(&self, _lua: &'a State) -> crate::Result<crate::Value<'a>> {
|
||||||
Ok(crate::Value::Function(self.clone()))
|
Ok(crate::Value::Function(self.clone()))
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> FromLua<'a> for Function<'a> {
|
|
||||||
fn from_lua(_lua: &'a State, val: Value<'a>) -> crate::Result<Self> {
|
|
||||||
val.into_function()
|
|
||||||
}
|
|
||||||
}
|
}
|
135
src/lib.rs
135
src/lib.rs
|
@ -1,135 +0,0 @@
|
||||||
use mlua_sys as lua;
|
|
||||||
|
|
||||||
pub mod state;
|
|
||||||
use state::*;
|
|
||||||
|
|
||||||
pub mod table;
|
|
||||||
use table::*;
|
|
||||||
|
|
||||||
pub mod function;
|
|
||||||
use function::*;
|
|
||||||
|
|
||||||
pub mod value;
|
|
||||||
use value::*;
|
|
||||||
|
|
||||||
pub mod guard;
|
|
||||||
use guard::*;
|
|
||||||
|
|
||||||
pub mod userdata;
|
|
||||||
use userdata::*;
|
|
||||||
|
|
||||||
pub mod util;
|
|
||||||
use util::*;
|
|
||||||
|
|
||||||
pub mod chunk;
|
|
||||||
use chunk::*;
|
|
||||||
|
|
||||||
pub mod error;
|
|
||||||
use error::*;
|
|
||||||
|
|
||||||
pub mod lref;
|
|
||||||
use lref::*;
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
pub mod tests;
|
|
||||||
|
|
||||||
pub trait PushToLuaStack<'a> {
|
|
||||||
unsafe fn push_to_lua_stack(&self, state: &'a State) -> Result<()>;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait FromLuaStack<'a>: Sized {
|
|
||||||
unsafe fn from_lua_stack(state: &'a State) -> Result<Self>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> PushToLuaStack<'a> for () {
|
|
||||||
unsafe fn push_to_lua_stack(&self, _state: &'a State) -> Result<()> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T: PushToLuaStack<'a>> PushToLuaStack<'a> for Option<T> {
|
|
||||||
unsafe fn push_to_lua_stack(&self, state: &'a State) -> Result<()> {
|
|
||||||
if let Some(v) = self {
|
|
||||||
v.push_to_lua_stack(state)?;
|
|
||||||
} else {
|
|
||||||
unsafe {
|
|
||||||
lua::lua_pushnil(state.state_ptr());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Implements PushToLuaStack for a number
|
|
||||||
macro_rules! impl_as_lua_number {
|
|
||||||
($ty: ident) => {
|
|
||||||
impl<'a> AsLua<'a> for $ty {
|
|
||||||
fn as_lua(&self, _lua: &'a State) -> crate::Result<Value<'a>> {
|
|
||||||
Ok(Value::Number(*self as f64))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> FromLua<'a> for $ty {
|
|
||||||
fn from_lua(_lua: &State, val: Value) -> crate::Result<Self> {
|
|
||||||
match val {
|
|
||||||
Value::Number(v) => Ok(v as $ty),
|
|
||||||
_ => Err(Error::UnexpectedType(
|
|
||||||
"Number".to_string(),
|
|
||||||
val.type_name().to_string(),
|
|
||||||
)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> PushToLuaStack<'a> for $ty {
|
|
||||||
unsafe fn push_to_lua_stack(&self, state: &'a State) -> crate::Result<()> {
|
|
||||||
let v = self.as_lua(state)?;
|
|
||||||
v.push_to_lua_stack(state)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
impl_as_lua_number!(i8);
|
|
||||||
impl_as_lua_number!(i16);
|
|
||||||
impl_as_lua_number!(i32);
|
|
||||||
impl_as_lua_number!(i64);
|
|
||||||
|
|
||||||
impl_as_lua_number!(u8);
|
|
||||||
impl_as_lua_number!(u16);
|
|
||||||
impl_as_lua_number!(u32);
|
|
||||||
impl_as_lua_number!(u64);
|
|
||||||
|
|
||||||
impl_as_lua_number!(f32);
|
|
||||||
impl_as_lua_number!(f64);
|
|
||||||
|
|
||||||
impl<'a> PushToLuaStack<'a> for String {
|
|
||||||
unsafe fn push_to_lua_stack(&self, state: &State) -> Result<()> {
|
|
||||||
state.ensure_stack(1)?;
|
|
||||||
|
|
||||||
self.as_str()
|
|
||||||
.push_to_lua_stack(state)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> FromLua<'a> for String {
|
|
||||||
fn from_lua(_lua: &State, val: Value) -> crate::Result<Self> {
|
|
||||||
val.as_string().cloned()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> PushToLuaStack<'a> for &str {
|
|
||||||
unsafe fn push_to_lua_stack(&self, state: &State) -> Result<()> {
|
|
||||||
state.ensure_stack(1)?;
|
|
||||||
|
|
||||||
let s = format!("{}\0", self);
|
|
||||||
let cstr = s.as_ptr() as *const i8;
|
|
||||||
// lua copies the string, so its okay if the string pointer is dropped
|
|
||||||
lua::lua_pushstring(state.state_ptr(), cstr);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
65
src/lref.rs
65
src/lref.rs
|
@ -1,65 +0,0 @@
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use crate::{Error, PushToLuaStack, Result, State};
|
|
||||||
|
|
||||||
use mlua_sys as lua;
|
|
||||||
|
|
||||||
/// A LuaRef is a a handle to something in Lua.
|
|
||||||
///
|
|
||||||
/// These are created with `luaL_ref`, they can be created from anything on the top of the stack
|
|
||||||
/// with [`LuaRef::from_stack`]. The references are automatically freed with `luaL_unref` when
|
|
||||||
/// the inner Arc detects a single strong count.
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct LuaRef<'a> {
|
|
||||||
lref: Arc<i32>,
|
|
||||||
state: &'a State,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Drop for LuaRef<'a> {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
unsafe {
|
|
||||||
let s = self.state.state_ptr();
|
|
||||||
|
|
||||||
if Arc::strong_count(&self.lref) == 1 {
|
|
||||||
lua::luaL_unref(s, lua::LUA_REGISTRYINDEX, *self.lref);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> LuaRef<'a> {
|
|
||||||
pub fn new(lua_ref: i32, state: &'a State) -> Self {
|
|
||||||
Self {
|
|
||||||
lref: Arc::new(lua_ref),
|
|
||||||
state,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a reference to what is at the top of the stack.
|
|
||||||
pub unsafe fn from_stack(state: &'a State) -> Result<Self> {
|
|
||||||
let s = state.state_ptr();
|
|
||||||
let r = lua::luaL_ref(s, lua::LUA_REGISTRYINDEX);
|
|
||||||
if r == lua::LUA_REFNIL {
|
|
||||||
Err(Error::Nil)
|
|
||||||
} else {
|
|
||||||
Ok(LuaRef::new(r, state))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> PushToLuaStack<'a> for LuaRef<'a> {
|
|
||||||
unsafe fn push_to_lua_stack(&self, state: &State) -> Result<()> {
|
|
||||||
let s = state.state_ptr();
|
|
||||||
|
|
||||||
state.ensure_stack(1)?;
|
|
||||||
let top = lua::lua_gettop(s);
|
|
||||||
let ty = lua::lua_rawgeti(s, lua::LUA_REGISTRYINDEX, *self.lref as i64);
|
|
||||||
let new_top = lua::lua_gettop(s);
|
|
||||||
|
|
||||||
if ty == lua::LUA_TNIL || ty == lua::LUA_TNONE || top == new_top {
|
|
||||||
return Err(Error::Nil);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,465 @@
|
||||||
|
use std::{cell::Ref, marker::PhantomData, sync::Arc};
|
||||||
|
|
||||||
|
use mlua_sys as lua;
|
||||||
|
|
||||||
|
pub mod state;
|
||||||
|
use state::*;
|
||||||
|
|
||||||
|
pub mod table;
|
||||||
|
use table::*;
|
||||||
|
|
||||||
|
pub mod function;
|
||||||
|
use function::*;
|
||||||
|
|
||||||
|
pub mod value;
|
||||||
|
use value::*;
|
||||||
|
|
||||||
|
pub mod guard;
|
||||||
|
use guard::*;
|
||||||
|
|
||||||
|
pub mod userdata;
|
||||||
|
use userdata::*;
|
||||||
|
|
||||||
|
pub mod util;
|
||||||
|
use util::*;
|
||||||
|
|
||||||
|
pub mod chunk;
|
||||||
|
use chunk::*;
|
||||||
|
|
||||||
|
fn main() -> Result<()> {
|
||||||
|
let lua = State::new();
|
||||||
|
lua.expose_libraries(&[StdLibrary::Debug, StdLibrary::Package]);
|
||||||
|
|
||||||
|
let globals = lua.globals()?;
|
||||||
|
|
||||||
|
let a = |_lua: &State, (num,): (i32,)| -> Result<i32> {
|
||||||
|
println!("Rust got number from lua: {}", num);
|
||||||
|
Ok(999)
|
||||||
|
};
|
||||||
|
|
||||||
|
let f = lua.create_function(a)?;
|
||||||
|
globals.set("native_test", f)?;
|
||||||
|
|
||||||
|
let ud = lua.create_userdata(UserdataProxy::<Vec2>::new())?;
|
||||||
|
globals.set("Vec2", ud)?;
|
||||||
|
|
||||||
|
let tbl = lua.create_table()?;
|
||||||
|
tbl.set("x", 10)?;
|
||||||
|
|
||||||
|
//let globals = lua.globals()?;
|
||||||
|
globals.set("X", tbl)?;
|
||||||
|
|
||||||
|
let chunk = lua.load("text.lua", r#"
|
||||||
|
require "util"
|
||||||
|
|
||||||
|
--[[function dump_table(tbl)
|
||||||
|
for k, v in pairs(tbl) do
|
||||||
|
if type(v) == "table" then
|
||||||
|
dump_table(v)
|
||||||
|
elseif type(v) == "function" then
|
||||||
|
|
||||||
|
else
|
||||||
|
print(k .. "=" .. tostring(v))
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
for k, v in pairs(_G) do
|
||||||
|
--print("Found global named " .. k)
|
||||||
|
|
||||||
|
if k == "X" then
|
||||||
|
--dump_table(v)
|
||||||
|
end
|
||||||
|
end]]--
|
||||||
|
|
||||||
|
function multiply_print(a, b)
|
||||||
|
print(a .. " * " .. b .. " = " .. a*b)
|
||||||
|
end
|
||||||
|
|
||||||
|
function multiply_ret(a, b)
|
||||||
|
return a * b
|
||||||
|
end
|
||||||
|
|
||||||
|
function say_number(a)
|
||||||
|
print("Lua says " .. a)
|
||||||
|
end
|
||||||
|
|
||||||
|
cool_num = 50
|
||||||
|
|
||||||
|
print("Lua is about to exec native_test")
|
||||||
|
local res = native_test(50)
|
||||||
|
print("Lua got " .. res .. " back from rust!")
|
||||||
|
|
||||||
|
print("Vec2: " .. dump_table(Vec2))
|
||||||
|
print("Meta Vec2: " .. dump_table(getmetatable(Vec2)))
|
||||||
|
|
||||||
|
--print("Vec2 is (" .. Vec2.x .. ", " .. Vec2.y .. ")")
|
||||||
|
|
||||||
|
local v1 = Vec2.new(50, 50)
|
||||||
|
print("v1 is (" .. v1.x .. ", " .. v1.y .. ")")
|
||||||
|
|
||||||
|
local v2 = Vec2.new(500, 500)
|
||||||
|
print("v2 is (" .. v2.x .. ", " .. v2.y .. ")")
|
||||||
|
|
||||||
|
local v_add = v1 + v2
|
||||||
|
v_add.x = 90
|
||||||
|
print("v_add is (" .. v_add.x .. ", " .. v_add.y .. ")")
|
||||||
|
|
||||||
|
function f1(v1, v2)
|
||||||
|
--print("f1 tb: " .. debug.traceback(1))
|
||||||
|
local v_add = v1:add(v2)
|
||||||
|
end
|
||||||
|
|
||||||
|
function f2(v1, v2)
|
||||||
|
--print("f2 tb: " .. debug.traceback(1))
|
||||||
|
f1(v1, v2)
|
||||||
|
end
|
||||||
|
|
||||||
|
function f3(v1, v2)
|
||||||
|
--print("f3 tb: " .. debug.traceback(1))
|
||||||
|
f2(v1, v2)
|
||||||
|
end
|
||||||
|
|
||||||
|
--f3(v1, v2)
|
||||||
|
"#)?;
|
||||||
|
|
||||||
|
// I don't care about the result of this execution, so I set the result as a
|
||||||
|
// Value which can be anything
|
||||||
|
//
|
||||||
|
// Chunks can also be executed with: `chunk.execute(())?;`
|
||||||
|
let res = lua.execute_chunk::<_, Value>(&chunk, ());
|
||||||
|
|
||||||
|
// if unwrapped, the new lines in the message would be escaped making
|
||||||
|
// the traceback in the error difficult to read.
|
||||||
|
if let Err(e) = res {
|
||||||
|
panic!("{}", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
assert_eq!(lua::lua_gettop(lua.state_ptr()), 0); // ensure that nothing is left on the stack
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A LuaRef is a a handle to something in Lua.
|
||||||
|
///
|
||||||
|
/// These are created with `luaL_ref`, they can be created from anything on the top of the stack
|
||||||
|
/// with [`LuaRef::from_stack`]. The references are automatically freed with `luaL_unref` when
|
||||||
|
/// the inner Arc detects a single strong count.
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct LuaRef<'a>(Arc<i32>, &'a State);
|
||||||
|
|
||||||
|
impl<'a> Drop for LuaRef<'a> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
let s = self.1.state_ptr();
|
||||||
|
|
||||||
|
if Arc::strong_count(&self.0) == 1 {
|
||||||
|
lua::luaL_unref(s, lua::LUA_REGISTRYINDEX, *self.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> LuaRef<'a> {
|
||||||
|
pub fn new(lua_ref: i32, state: &'a State) -> Self {
|
||||||
|
Self(Arc::new(lua_ref), state)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a reference to what is at the top of the stack.
|
||||||
|
pub unsafe fn from_stack(state: &'a State) -> Result<Self> {
|
||||||
|
let s = state.state_ptr();
|
||||||
|
|
||||||
|
let r = lua::luaL_ref(s, lua::LUA_REGISTRYINDEX);
|
||||||
|
if r == lua::LUA_REFNIL {
|
||||||
|
Err(Error::Nil)
|
||||||
|
} else {
|
||||||
|
Ok(LuaRef::new(r, state))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> PushToLuaStack<'a> for LuaRef<'a> {
|
||||||
|
unsafe fn push_to_lua_stack(&self, state: &State) -> Result<()> {
|
||||||
|
let s = state.state_ptr();
|
||||||
|
|
||||||
|
state.ensure_stack(1)?;
|
||||||
|
let top = lua::lua_gettop(s);
|
||||||
|
let ty = lua::lua_rawgeti(s, lua::LUA_REGISTRYINDEX, *self.0 as i64);
|
||||||
|
let new_top = lua::lua_gettop(s);
|
||||||
|
|
||||||
|
if ty == lua::LUA_TNIL || ty == lua::LUA_TNONE || top == new_top {
|
||||||
|
return Err(Error::Nil);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
pub enum Error {
|
||||||
|
#[error("Syntax error: {0}")]
|
||||||
|
Syntax(String),
|
||||||
|
/// An error returned from lua
|
||||||
|
#[error("Lua runtime error: {0}")]
|
||||||
|
Runtime(String),
|
||||||
|
#[error("Error when running a __gc metamethod")]
|
||||||
|
GcFailure(String),
|
||||||
|
/// Ran into a not enough memory error when trying to grow the lua stack.
|
||||||
|
#[error("Failed to allocate memory")]
|
||||||
|
MemoryAlloc,
|
||||||
|
#[error("Ran into a nill value on the stack")]
|
||||||
|
Nil,
|
||||||
|
#[error("Unexpected type, expected {0} but got {1}")]
|
||||||
|
UnexpectedType(String, String),
|
||||||
|
#[error("bad argument #{arg_index}{} to `{}` ({error})",
|
||||||
|
.arg_name.clone().map(|a| format!(" (name: {})", a)).unwrap_or("".to_string()),
|
||||||
|
.func.clone().unwrap_or("Unknown".to_string())
|
||||||
|
)]
|
||||||
|
BadArgument {
|
||||||
|
func: Option<String>,
|
||||||
|
arg_index: i32,
|
||||||
|
arg_name: Option<String>,
|
||||||
|
/// the error that describes what was wrong for this argument
|
||||||
|
#[source]
|
||||||
|
error: Arc<Error>
|
||||||
|
},
|
||||||
|
#[error("Incorrect number of arguments, expected {arg_expected}, got {arg_count}")]
|
||||||
|
IncorrectArgCount {
|
||||||
|
arg_expected: i32,
|
||||||
|
arg_count: i32,
|
||||||
|
},
|
||||||
|
#[error("There is already a registry entry with the key {0}")]
|
||||||
|
RegistryConflict(String),
|
||||||
|
#[error("Userdata types did not match")]
|
||||||
|
UserdataMismatch,
|
||||||
|
#[error("Missing meta table for userdata")]
|
||||||
|
MissingMetatable,
|
||||||
|
#[error("An error occurred when attempting to convert from a ValueVec at value index {value_idx}, cause: {error}")]
|
||||||
|
ValueVecError {
|
||||||
|
value_idx: i32,
|
||||||
|
#[source]
|
||||||
|
error: Arc<Error>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error {
|
||||||
|
pub fn runtime(msg: &str) -> Self {
|
||||||
|
Self::Runtime(msg.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unexpected_type(expected: &str, got: &str) -> Self {
|
||||||
|
Self::UnexpectedType(expected.to_string(), got.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Throw the error in lua.
|
||||||
|
///
|
||||||
|
/// This method never returns
|
||||||
|
pub unsafe fn throw_lua(self, lua: *mut lua::lua_State) -> ! {
|
||||||
|
let msg = format!("{}\0", self);
|
||||||
|
let msg_c = msg.as_ptr() as *const i8;
|
||||||
|
lua::luaL_error(lua, msg_c);
|
||||||
|
unreachable!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A result for use with lua functions
|
||||||
|
type Result<T> = core::result::Result<T, Error>;
|
||||||
|
|
||||||
|
pub trait PushToLuaStack<'a> {
|
||||||
|
unsafe fn push_to_lua_stack(&self, state: &'a State) -> Result<()>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait FromLuaStack<'a>: Sized {
|
||||||
|
unsafe fn from_lua_stack(state: &'a State) -> Result<Self>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> PushToLuaStack<'a> for () {
|
||||||
|
unsafe fn push_to_lua_stack(&self, _state: &'a State) -> Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T: PushToLuaStack<'a>> PushToLuaStack<'a> for Option<T> {
|
||||||
|
unsafe fn push_to_lua_stack(&self, state: &'a State) -> Result<()> {
|
||||||
|
if let Some(v) = self {
|
||||||
|
v.push_to_lua_stack(state)?;
|
||||||
|
} else {
|
||||||
|
unsafe {
|
||||||
|
lua::lua_pushnil(state.state_ptr());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implements PushToLuaStack for a number
|
||||||
|
macro_rules! impl_as_lua_number {
|
||||||
|
($ty: ident) => {
|
||||||
|
impl<'a> AsLua<'a> for $ty {
|
||||||
|
fn as_lua(&self, _lua: &'a State) -> crate::Result<Value<'a>> {
|
||||||
|
Ok(Value::Number(*self as f64))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> FromLua<'a> for $ty {
|
||||||
|
fn from_lua(_lua: &State, val: Value) -> crate::Result<Self> {
|
||||||
|
match val {
|
||||||
|
Value::Number(v) => Ok(v as $ty),
|
||||||
|
_ => {
|
||||||
|
Err(Error::UnexpectedType("Number".to_string(), val.type_name().to_string()))
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> PushToLuaStack<'a> for $ty {
|
||||||
|
unsafe fn push_to_lua_stack(&self, state: &'a State) -> crate::Result<()> {
|
||||||
|
let v = self.as_lua(state)?;
|
||||||
|
v.push_to_lua_stack(state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_as_lua_number!(i8);
|
||||||
|
impl_as_lua_number!(i16);
|
||||||
|
impl_as_lua_number!(i32);
|
||||||
|
impl_as_lua_number!(i64);
|
||||||
|
|
||||||
|
impl_as_lua_number!(u8);
|
||||||
|
impl_as_lua_number!(u16);
|
||||||
|
impl_as_lua_number!(u32);
|
||||||
|
impl_as_lua_number!(u64);
|
||||||
|
|
||||||
|
impl_as_lua_number!(f32);
|
||||||
|
impl_as_lua_number!(f64);
|
||||||
|
|
||||||
|
impl<'a> PushToLuaStack<'a> for String {
|
||||||
|
unsafe fn push_to_lua_stack(&self, state: &State) -> Result<()> {
|
||||||
|
state.ensure_stack(1)?;
|
||||||
|
|
||||||
|
let s = format!("{}\0", self);
|
||||||
|
let cstr = s.as_ptr() as *const i8;
|
||||||
|
lua::lua_pushstring(state.state_ptr(), cstr);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> FromLua<'a> for String {
|
||||||
|
fn from_lua(_lua: &State, val: Value) -> crate::Result<Self> {
|
||||||
|
val.as_string().cloned()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> PushToLuaStack<'a> for &str {
|
||||||
|
unsafe fn push_to_lua_stack(&self, state: &State) -> Result<()> {
|
||||||
|
state.ensure_stack(1)?;
|
||||||
|
|
||||||
|
let s = format!("{}\0", self);
|
||||||
|
let cstr = s.as_ptr() as *const i8;
|
||||||
|
lua::lua_pushstring(state.state_ptr(), cstr);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub struct Vec3 {
|
||||||
|
x: f32,
|
||||||
|
y: f32,
|
||||||
|
z: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Userdata for Vec3 {
|
||||||
|
fn build<'a>(_builder: &mut UserdataBuilder<'a, Self>) -> crate::Result<()> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn name() -> String {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Vec2 {
|
||||||
|
x: f32,
|
||||||
|
y: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Userdata for Vec2 {
|
||||||
|
fn build<'a>(builder: &mut UserdataBuilder<'a, Vec2>) -> crate::Result<()> {
|
||||||
|
builder
|
||||||
|
.field_getter("x", |_, this| Ok(this.x))
|
||||||
|
.field_getter("y", |_, this| Ok(this.y))
|
||||||
|
|
||||||
|
.field_setter("x", |_, this, x: f32| this.x = x)
|
||||||
|
.field_setter("y", |_, this, y: f32| this.y = y)
|
||||||
|
|
||||||
|
.function("new", |lua, (x, y)| {
|
||||||
|
lua.create_userdata(Vec2 { x, y, })
|
||||||
|
})
|
||||||
|
|
||||||
|
// method test
|
||||||
|
.method("add", |lua, lhs: &Vec2, (rhs,): (Ref<Vec3>,)| {
|
||||||
|
let lx = lhs.x;
|
||||||
|
let ly = lhs.y;
|
||||||
|
|
||||||
|
let rx = rhs.x;
|
||||||
|
let ry = rhs.y;
|
||||||
|
|
||||||
|
lua.create_userdata(Vec2 { x: lx + rx, y: ly + ry, })
|
||||||
|
})
|
||||||
|
|
||||||
|
.meta_method(MetaMethod::Add, |lua, lhs: &Vec2, (rhs,): (Ref<Vec2>,)| {
|
||||||
|
let lx = lhs.x;
|
||||||
|
let ly = lhs.y;
|
||||||
|
|
||||||
|
let rx = rhs.x;
|
||||||
|
let ry = rhs.y;
|
||||||
|
|
||||||
|
lua.create_userdata(Vec2 { x: lx + rx, y: ly + ry, })
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn name() -> String {
|
||||||
|
"Vec2".to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct UserdataProxy<T: Userdata>(PhantomData<T>);
|
||||||
|
|
||||||
|
impl<T: Userdata> UserdataProxy<T> {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self(PhantomData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T: Userdata> AsLua<'a> for UserdataProxy<T> {
|
||||||
|
fn as_lua(&self, _lua: &'a State) -> crate::Result<Value<'a>> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Userdata> Userdata for UserdataProxy<T> {
|
||||||
|
fn build<'a>(builder: &mut UserdataBuilder<'a, Self>) -> crate::Result<()> {
|
||||||
|
let mut other = UserdataBuilder::<T>::new();
|
||||||
|
T::build(&mut other)?;
|
||||||
|
|
||||||
|
// only the functions need to be added since they're the only thing usable from a proxy
|
||||||
|
builder.functions = other.functions;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn name() -> String {
|
||||||
|
let name = format!("{}Proxy", T::name());
|
||||||
|
name
|
||||||
|
}
|
||||||
|
}
|
149
src/state.rs
149
src/state.rs
|
@ -1,10 +1,9 @@
|
||||||
use core::ffi;
|
use core::ffi;
|
||||||
use std::{alloc::{self, Layout}, any::TypeId, cell::RefCell, collections::HashMap, ffi::{CStr, CString}, mem, ptr::{self, NonNull}, str::Utf8Error, sync::Arc};
|
use std::{alloc::{self, Layout}, any::TypeId, cell::RefCell, collections::HashMap, ffi::{CStr, CString}, mem, ptr::{self, NonNull}, str::Utf8Error, sync::Arc};
|
||||||
|
|
||||||
use lua::lua_gc;
|
|
||||||
use mlua_sys as lua;
|
use mlua_sys as lua;
|
||||||
|
|
||||||
use crate::{lua_error_guard, proxy::UserdataProxy, AnyUserdata, AsLua, Chunk, Error, FromLua, FromLuaStack, FromLuaVec, Function, IntoChunkData, IntoLuaVec, LuaRef, MetaMethod, PushToLuaStack, Result, StackGuard, Table, Userdata, UserdataBuilder, Value, ValueVec};
|
use crate::{lua_error_guard, AnyUserdata, IntoChunkData, AsLua, Chunk, Error, FromLua, FromLuaStack, FromLuaVec, Function, LuaRef, MetaMethod, PushToLuaStack, Result, StackGuard, Table, Userdata, UserdataBuilder, Value, ValueVec};
|
||||||
|
|
||||||
pub fn ptr_to_string(ptr: *const i8) -> std::result::Result<String, Utf8Error> {
|
pub fn ptr_to_string(ptr: *const i8) -> std::result::Result<String, Utf8Error> {
|
||||||
let c = unsafe { CStr::from_ptr(ptr) };
|
let c = unsafe { CStr::from_ptr(ptr) };
|
||||||
|
@ -89,8 +88,8 @@ struct ClosureData<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct ExtraSpace<'a> {
|
struct ExtraSpace<'a> {
|
||||||
pub userdata_metatables: HashMap<TypeId, LuaRef<'a>>,
|
userdata_metatables: HashMap<TypeId, LuaRef<'a>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct State {
|
pub struct State {
|
||||||
|
@ -102,17 +101,10 @@ impl Drop for State {
|
||||||
unsafe {
|
unsafe {
|
||||||
let extra = self.get_extra_space_ptr();
|
let extra = self.get_extra_space_ptr();
|
||||||
|
|
||||||
{
|
|
||||||
// clear the refs to anything in lua before we close it and
|
|
||||||
// attempt to drop extra after
|
|
||||||
let extra = &mut *extra;
|
|
||||||
extra.userdata_metatables.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
lua::lua_close(self.lua.as_ptr());
|
lua::lua_close(self.lua.as_ptr());
|
||||||
extra.drop_in_place();
|
extra.drop_in_place();
|
||||||
|
|
||||||
// must be dealloced since it wasn't memory created from lua (i.e. userdata)
|
// must be dealloced since it wasn't memory created from lua (like userdata)
|
||||||
alloc::dealloc(extra.cast(), Layout::new::<ExtraSpace>());
|
alloc::dealloc(extra.cast(), Layout::new::<ExtraSpace>());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -160,7 +152,7 @@ impl State {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_extra_space(&self) -> &mut ExtraSpace {
|
fn get_extra_space(&self) -> &mut ExtraSpace {
|
||||||
unsafe {
|
unsafe {
|
||||||
self.get_extra_space_ptr().as_mut()
|
self.get_extra_space_ptr().as_mut()
|
||||||
.expect("Somehow the Lua state's extra data got deleted!")
|
.expect("Somehow the Lua state's extra data got deleted!")
|
||||||
|
@ -220,7 +212,7 @@ impl State {
|
||||||
|
|
||||||
pub fn execute_chunk<'a, A, R>(&'a self, chunk: &'a Chunk, args: A) -> Result<R>
|
pub fn execute_chunk<'a, A, R>(&'a self, chunk: &'a Chunk, args: A) -> Result<R>
|
||||||
where
|
where
|
||||||
A: IntoLuaVec<'a>,
|
A: AsLua<'a>,
|
||||||
R: FromLua<'a>
|
R: FromLua<'a>
|
||||||
{
|
{
|
||||||
let handler = self.create_function(|lua, msg: String| {
|
let handler = self.create_function(|lua, msg: String| {
|
||||||
|
@ -439,7 +431,7 @@ impl State {
|
||||||
|
|
||||||
pub(crate) fn create_userdata_metatable<'a, T: Userdata + 'static>(&'a self) -> Result<Table<'a>> {
|
pub(crate) fn create_userdata_metatable<'a, T: Userdata + 'static>(&'a self) -> Result<Table<'a>> {
|
||||||
let mut builder = UserdataBuilder::<T>::new();
|
let mut builder = UserdataBuilder::<T>::new();
|
||||||
T::build(self, &mut builder)?;
|
T::build(&mut builder)?;
|
||||||
|
|
||||||
let getters = builder.field_getters;
|
let getters = builder.field_getters;
|
||||||
let setters = builder.field_setters;
|
let setters = builder.field_setters;
|
||||||
|
@ -504,16 +496,22 @@ impl State {
|
||||||
|
|
||||||
// if a Gc metamethod was not manually defined, set it
|
// if a Gc metamethod was not manually defined, set it
|
||||||
if !mt.raw_has_key(MetaMethod::Gc)? {
|
if !mt.raw_has_key(MetaMethod::Gc)? {
|
||||||
let gc_func = self.create_function(|_lua, ud: AnyUserdata| {
|
let gc_func = self.create_function(move |lua: &State, ud: AnyUserdata| {
|
||||||
unsafe {
|
unsafe {
|
||||||
let ud_ptr = ud.as_ptr_unchecked::<T>()?;
|
// if this panics, there's a weird bug.
|
||||||
|
let ud_ptr = ud.as_ptr_unchecked::<T>().unwrap();
|
||||||
ud_ptr.drop_in_place();
|
ud_ptr.drop_in_place();
|
||||||
|
|
||||||
|
lua::luaL_unref(lua.state_ptr(), lua::LUA_REGISTRYINDEX, *ud.lref.0);
|
||||||
|
|
||||||
|
let extra = lua.get_extra_space();
|
||||||
|
extra.userdata_metatables.remove(&TypeId::of::<T>());
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
mt.set(MetaMethod::Gc, gc_func)?;
|
mt.set_meta(MetaMethod::Gc, gc_func)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let extra = self.get_extra_space();
|
let extra = self.get_extra_space();
|
||||||
|
@ -522,13 +520,6 @@ impl State {
|
||||||
Ok(mt)
|
Ok(mt)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a userdata proxy. This proxy has the same metatable as `T`, but has no default value.
|
|
||||||
/// This eliminates the need for `T` to have a default value.
|
|
||||||
pub fn create_proxy<T: Userdata + 'static>(&self) -> Result<AnyUserdata> {
|
|
||||||
self.create_userdata(UserdataProxy::<T>::new())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Retrieves the debug info from the Lua stack.
|
|
||||||
pub(crate) unsafe fn debug_info(&self) -> Box<lua::lua_Debug> {
|
pub(crate) unsafe fn debug_info(&self) -> Box<lua::lua_Debug> {
|
||||||
let s = self.state_ptr();
|
let s = self.state_ptr();
|
||||||
|
|
||||||
|
@ -542,7 +533,6 @@ impl State {
|
||||||
Box::from_raw(ar)
|
Box::from_raw(ar)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieves the current line of the script
|
|
||||||
pub fn current_line(&self) -> usize {
|
pub fn current_line(&self) -> usize {
|
||||||
unsafe {
|
unsafe {
|
||||||
let ar = self.debug_info();
|
let ar = self.debug_info();
|
||||||
|
@ -550,7 +540,6 @@ impl State {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieves a traceback of the Lua stack, optionally prepends the traceback string with `msg`
|
|
||||||
pub fn traceback(&self, msg: Option<&str>) -> Result<String> {
|
pub fn traceback(&self, msg: Option<&str>) -> Result<String> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let _g = StackGuard::new(self);
|
let _g = StackGuard::new(self);
|
||||||
|
@ -587,108 +576,4 @@ impl State {
|
||||||
Ok(s)
|
Ok(s)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a boolean indiciating if the Lua garbage collector is running.
|
|
||||||
pub fn is_gc_running(&self) -> Result<bool> {
|
|
||||||
unsafe {
|
|
||||||
let s = self.state_ptr();
|
|
||||||
|
|
||||||
Ok(lua_gc(s, lua::LUA_GCISRUNNING) != 0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Triggers a full garbage-collection cycle.
|
|
||||||
pub fn gc_collect(&self) {
|
|
||||||
unsafe {
|
|
||||||
let s = self.state_ptr();
|
|
||||||
|
|
||||||
lua_gc(s, lua::LUA_GCCOLLECT);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Disable the garbage collector
|
|
||||||
pub fn gc_stop(&self) {
|
|
||||||
unsafe {
|
|
||||||
let s = self.state_ptr();
|
|
||||||
|
|
||||||
lua_gc(s, lua::LUA_GCSTOP);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Restarts the garbage collector
|
|
||||||
pub fn gc_restart(&self) {
|
|
||||||
unsafe {
|
|
||||||
let s = self.state_ptr();
|
|
||||||
|
|
||||||
lua_gc(s, lua::LUA_GCRESTART);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the current amount of memory used by Lua in kilabytes
|
|
||||||
pub fn memory_usage(&self) -> u32 {
|
|
||||||
unsafe {
|
|
||||||
let s = self.state_ptr();
|
|
||||||
|
|
||||||
lua_gc(s, lua::LUA_GCCOUNT) as u32
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the remainder of dividing the current amount of bytes of memory in use by Lua by 1024.
|
|
||||||
pub fn memory_usage_rem(&self) -> u32 {
|
|
||||||
unsafe {
|
|
||||||
let s = self.state_ptr();
|
|
||||||
|
|
||||||
lua_gc(s, lua::LUA_GCCOUNTB) as u32
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Trigger a step of the garbage collector
|
|
||||||
///
|
|
||||||
/// Lua docs:
|
|
||||||
/// > Performs an incremental step of garbage collection, corresponding to the allocation
|
|
||||||
/// of stepsize Kbytes.
|
|
||||||
pub fn gc_step(&self, step_size: i32) {
|
|
||||||
unsafe {
|
|
||||||
let s = self.state_ptr();
|
|
||||||
|
|
||||||
lua_gc(s, lua::LUA_GCSTEP, step_size);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Changes the collector to incremental mode with the given parameters
|
|
||||||
/// (see [Incremental Garbage Collection](https://www.lua.org/manual/5.4/manual.html#2.5.1)).
|
|
||||||
/// Returns the previous mode.
|
|
||||||
pub fn gc_set_incremental(&self, pause: i32, step_mul: i32, step_size: i32) -> GcMode {
|
|
||||||
unsafe {
|
|
||||||
let s = self.state_ptr();
|
|
||||||
|
|
||||||
let mode = lua_gc(s, lua::LUA_GCINC, pause, step_mul, step_size);
|
|
||||||
if mode == lua::LUA_GCGEN {
|
|
||||||
GcMode::Generational
|
|
||||||
} else {
|
|
||||||
GcMode::Incremental
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Changes the collector to generational mode with the given parameters
|
|
||||||
/// (see [Generational Garbage Collection](https://www.lua.org/manual/5.4/manual.html#2.5.2)).
|
|
||||||
/// Returns the previous mode.
|
|
||||||
pub fn gc_set_generational(&self, minor_mul: i32, major_mul: i32)-> GcMode {
|
|
||||||
unsafe {
|
|
||||||
let s = self.state_ptr();
|
|
||||||
|
|
||||||
let mode = lua_gc(s, lua::LUA_GCGEN, minor_mul, major_mul);
|
|
||||||
if mode == lua::LUA_GCGEN {
|
|
||||||
GcMode::Generational
|
|
||||||
} else {
|
|
||||||
GcMode::Incremental
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum GcMode {
|
|
||||||
Incremental,
|
|
||||||
Generational,
|
|
||||||
}
|
}
|
359
src/table.rs
359
src/table.rs
|
@ -1,13 +1,14 @@
|
||||||
|
|
||||||
use mlua_sys as lua;
|
use mlua_sys as lua;
|
||||||
|
|
||||||
use crate::{ensure_type, AsLua, FromLua, FromLuaStack, LuaRef, PushToLuaStack, Result, StackGuard, State, Value};
|
use crate::{ensure_type, FromLuaStack, LuaRef, PushToLuaStack, Result, StackGuard, State};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Table<'a> {
|
pub struct Table<'a> {
|
||||||
state: &'a State,
|
state: &'a State,
|
||||||
pub(crate) lref: LuaRef<'a>,
|
pub(crate) lref: LuaRef<'a>,
|
||||||
/// a boolean indicating if this Table is a Metatable
|
/// a boolean indicating if this Table is a Metatable
|
||||||
pub(crate) mt_marker: bool,
|
mt_marker: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Table<'a> {
|
impl<'a> Table<'a> {
|
||||||
|
@ -82,8 +83,8 @@ impl<'a> Table<'a> {
|
||||||
/// This may trigger the `__newindex` metamethod, see [`Table::raw_set`]
|
/// This may trigger the `__newindex` metamethod, see [`Table::raw_set`]
|
||||||
pub fn set<K, V>(&self, key: K, val: V) -> Result<()>
|
pub fn set<K, V>(&self, key: K, val: V) -> Result<()>
|
||||||
where
|
where
|
||||||
K: AsLua<'a>,
|
K: PushToLuaStack<'a>,
|
||||||
V: AsLua<'a>
|
V: PushToLuaStack<'a>
|
||||||
{
|
{
|
||||||
let s = self.state.state_ptr();
|
let s = self.state.state_ptr();
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -96,10 +97,8 @@ impl<'a> Table<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
self.lref.push_to_lua_stack(self.state)?;
|
self.lref.push_to_lua_stack(self.state)?;
|
||||||
key.as_lua(self.state)?
|
key.push_to_lua_stack(self.state)?;
|
||||||
.push_to_lua_stack(self.state)?;
|
val.push_to_lua_stack(self.state)?;
|
||||||
val.as_lua(self.state)?
|
|
||||||
.push_to_lua_stack(self.state)?;
|
|
||||||
|
|
||||||
lua::lua_settable(s, -3);
|
lua::lua_settable(s, -3);
|
||||||
}
|
}
|
||||||
|
@ -112,8 +111,8 @@ impl<'a> Table<'a> {
|
||||||
/// This may trigger the `__index` metamethod, see [`Table::raw_get`]
|
/// This may trigger the `__index` metamethod, see [`Table::raw_get`]
|
||||||
pub fn get<K, V>(&self, key: K) -> Result<V>
|
pub fn get<K, V>(&self, key: K) -> Result<V>
|
||||||
where
|
where
|
||||||
K: AsLua<'a>,
|
K: PushToLuaStack<'a>,
|
||||||
V: FromLua<'a>,
|
V: FromLuaStack<'a>,
|
||||||
{
|
{
|
||||||
let s = self.state.state_ptr();
|
let s = self.state.state_ptr();
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -121,13 +120,10 @@ impl<'a> Table<'a> {
|
||||||
let _g = StackGuard::new(self.state);
|
let _g = StackGuard::new(self.state);
|
||||||
|
|
||||||
self.lref.push_to_lua_stack(self.state)?;
|
self.lref.push_to_lua_stack(self.state)?;
|
||||||
let val = key.as_lua(self.state)?;
|
key.push_to_lua_stack(self.state)?;
|
||||||
val.push_to_lua_stack(self.state)?;
|
|
||||||
|
|
||||||
lua::lua_gettable(s, -2); // table[key] is at top of stack
|
lua::lua_gettable(s, -2); // table[key] is at top of stack
|
||||||
|
|
||||||
let val = Value::from_lua_stack(self.state)?;
|
let val = V::from_lua_stack(self.state)?;
|
||||||
let val = V::from_lua(self.state, val)?;
|
|
||||||
|
|
||||||
Ok(val)
|
Ok(val)
|
||||||
}
|
}
|
||||||
|
@ -154,7 +150,7 @@ impl<'a> Table<'a> {
|
||||||
/// Returns a boolean indicating if this table has a key
|
/// Returns a boolean indicating if this table has a key
|
||||||
pub fn has_key<K>(&self, key: K) -> Result<bool>
|
pub fn has_key<K>(&self, key: K) -> Result<bool>
|
||||||
where
|
where
|
||||||
K: AsLua<'a>,
|
K: PushToLuaStack<'a>,
|
||||||
{
|
{
|
||||||
let s = self.state.state_ptr();
|
let s = self.state.state_ptr();
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -162,8 +158,7 @@ impl<'a> Table<'a> {
|
||||||
let _g = StackGuard::new(self.state);
|
let _g = StackGuard::new(self.state);
|
||||||
|
|
||||||
self.lref.push_to_lua_stack(self.state)?;
|
self.lref.push_to_lua_stack(self.state)?;
|
||||||
let key_val = key.as_lua(self.state)?;
|
key.push_to_lua_stack(self.state)?;
|
||||||
key_val.push_to_lua_stack(self.state)?;
|
|
||||||
|
|
||||||
// table[key] is at top of stack
|
// table[key] is at top of stack
|
||||||
if lua::lua_gettable(s, -2) == lua::LUA_TNIL {
|
if lua::lua_gettable(s, -2) == lua::LUA_TNIL {
|
||||||
|
@ -177,8 +172,8 @@ impl<'a> Table<'a> {
|
||||||
/// Set a key to a value in the table without calling any meta methods.
|
/// Set a key to a value in the table without calling any meta methods.
|
||||||
pub fn raw_set<K, V>(&self, key: K, val: V) -> Result<()>
|
pub fn raw_set<K, V>(&self, key: K, val: V) -> Result<()>
|
||||||
where
|
where
|
||||||
K: AsLua<'a>,
|
K: PushToLuaStack<'a>,
|
||||||
V: AsLua<'a>
|
V: PushToLuaStack<'a>
|
||||||
{
|
{
|
||||||
let s = self.state.state_ptr();
|
let s = self.state.state_ptr();
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -186,10 +181,8 @@ impl<'a> Table<'a> {
|
||||||
let _g = StackGuard::new(self.state);
|
let _g = StackGuard::new(self.state);
|
||||||
|
|
||||||
self.lref.push_to_lua_stack(self.state)?;
|
self.lref.push_to_lua_stack(self.state)?;
|
||||||
key.as_lua(self.state)?
|
key.push_to_lua_stack(self.state)?;
|
||||||
.push_to_lua_stack(self.state)?;
|
val.push_to_lua_stack(self.state)?;
|
||||||
val.as_lua(self.state)?
|
|
||||||
.push_to_lua_stack(self.state)?;
|
|
||||||
|
|
||||||
lua::lua_rawset(s, -3);
|
lua::lua_rawset(s, -3);
|
||||||
}
|
}
|
||||||
|
@ -198,10 +191,10 @@ impl<'a> Table<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a value from the table without calling any meta methods.
|
/// Get a value from the table without calling any meta methods.
|
||||||
pub fn raw_get<K, V>(&self, key: K) -> Result<V>
|
pub fn raw_get<K, V>(&'a self, key: K) -> Result<V>
|
||||||
where
|
where
|
||||||
K: AsLua<'a>,
|
K: PushToLuaStack<'a>,
|
||||||
V: FromLua<'a>,
|
V: FromLuaStack<'a>,
|
||||||
{
|
{
|
||||||
let s = self.state.state_ptr();
|
let s = self.state.state_ptr();
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -209,13 +202,9 @@ impl<'a> Table<'a> {
|
||||||
let _g = StackGuard::new(self.state);
|
let _g = StackGuard::new(self.state);
|
||||||
|
|
||||||
self.lref.push_to_lua_stack(self.state)?;
|
self.lref.push_to_lua_stack(self.state)?;
|
||||||
key.as_lua(self.state)?
|
key.push_to_lua_stack(self.state)?;
|
||||||
.push_to_lua_stack(self.state)?;
|
|
||||||
|
|
||||||
lua::lua_rawget(s, -2); // table[key] is at top of stack
|
lua::lua_rawget(s, -2); // table[key] is at top of stack
|
||||||
|
V::from_lua_stack(self.state)
|
||||||
let val = Value::from_lua_stack(self.state)?;
|
|
||||||
V::from_lua(self.state, val)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -237,7 +226,7 @@ impl<'a> Table<'a> {
|
||||||
/// Returns a boolean indicating if this table has a key without calling any meta methods.
|
/// Returns a boolean indicating if this table has a key without calling any meta methods.
|
||||||
pub fn raw_has_key<K>(&self, key: K) -> Result<bool>
|
pub fn raw_has_key<K>(&self, key: K) -> Result<bool>
|
||||||
where
|
where
|
||||||
K: AsLua<'a>,
|
K: PushToLuaStack<'a>,
|
||||||
{
|
{
|
||||||
let s = self.state.state_ptr();
|
let s = self.state.state_ptr();
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -245,9 +234,7 @@ impl<'a> Table<'a> {
|
||||||
let _g = StackGuard::new(self.state);
|
let _g = StackGuard::new(self.state);
|
||||||
|
|
||||||
self.lref.push_to_lua_stack(self.state)?;
|
self.lref.push_to_lua_stack(self.state)?;
|
||||||
key.as_lua(self.state)?
|
key.push_to_lua_stack(self.state)?;
|
||||||
.push_to_lua_stack(self.state)?;
|
|
||||||
|
|
||||||
// table[key] is at top of stack
|
// table[key] is at top of stack
|
||||||
Ok(lua::lua_rawget(s, -2) != lua::LUA_TNIL)
|
Ok(lua::lua_rawget(s, -2) != lua::LUA_TNIL)
|
||||||
}
|
}
|
||||||
|
@ -258,8 +245,8 @@ impl<'a> Table<'a> {
|
||||||
/// Does nothing if this table is not a metatable
|
/// Does nothing if this table is not a metatable
|
||||||
pub fn set_meta<K, V>(&self, key: K, val: V) -> Result<()>
|
pub fn set_meta<K, V>(&self, key: K, val: V) -> Result<()>
|
||||||
where
|
where
|
||||||
K: AsLua<'a>,
|
K: PushToLuaStack<'a>,
|
||||||
V: AsLua<'a>
|
V: PushToLuaStack<'a>
|
||||||
{
|
{
|
||||||
if self.mt_marker {
|
if self.mt_marker {
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -268,24 +255,14 @@ impl<'a> Table<'a> {
|
||||||
let _g = StackGuard::new(self.state);
|
let _g = StackGuard::new(self.state);
|
||||||
|
|
||||||
self.lref.push_to_lua_stack(self.state)?;
|
self.lref.push_to_lua_stack(self.state)?;
|
||||||
key.as_lua(self.state)?
|
key.push_to_lua_stack(self.state)?;
|
||||||
.push_to_lua_stack(self.state)?;
|
val.push_to_lua_stack(self.state)?;
|
||||||
val.as_lua(self.state)?
|
|
||||||
.push_to_lua_stack(self.state)?;
|
|
||||||
lua::lua_settable(s, -3);
|
lua::lua_settable(s, -3);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn try_into<T: TableProxy>(self) -> Result<T> {
|
|
||||||
T::from_table(self.state, self)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn try_from<T: TableProxy + Sized>(state: &'a State, proxy: T) -> Result<Table<'a>> {
|
|
||||||
proxy.as_table(state)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> PushToLuaStack<'a> for Table<'a> {
|
impl<'a> PushToLuaStack<'a> for Table<'a> {
|
||||||
|
@ -302,282 +279,4 @@ impl<'a> FromLuaStack<'a> for Table<'a> {
|
||||||
ensure_type(state, lua::LUA_TTABLE, -1)?;
|
ensure_type(state, lua::LUA_TTABLE, -1)?;
|
||||||
Table::with_ref(state, LuaRef::from_stack(state)?, false)
|
Table::with_ref(state, LuaRef::from_stack(state)?, false)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> FromLua<'a> for Table<'a> {
|
|
||||||
fn from_lua(_: &'a State, val: Value<'a>) -> crate::Result<Self> {
|
|
||||||
val.into_table()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> AsLua<'a> for Table<'a> {
|
|
||||||
fn as_lua(&self, _: &'a State) -> crate::Result<Value<'a>> {
|
|
||||||
Ok(Value::Table(self.clone()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This trait is used for proxying a Rust type to and from Lua as a Lua table. In Lua, you can
|
|
||||||
/// make a representation of your Rust type, then implement this trait on the Rust type. This
|
|
||||||
/// trait allows you to put your Rust type into Lua as the type you created in Lua, and you can
|
|
||||||
/// also retrieve an instance of your Rust type from an instance of the Lua type. This could help
|
|
||||||
/// with minimizing the amount of calls to and from Rust.
|
|
||||||
pub trait TableProxy: Sized {
|
|
||||||
/// Create an instance of `Self` from the table
|
|
||||||
fn from_table<'a>(lua: &'a State, table: Table<'a>) -> Result<Self>;
|
|
||||||
/// Creates a Lua instance from `Self`
|
|
||||||
fn as_table<'a>(&self, state: &'a State) -> Result<Table<'a>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A struct that wraps a Proxied value. It can be used to get
|
|
||||||
pub struct Proxy<T: TableProxy> {
|
|
||||||
data: Option<T>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: TableProxy> Proxy<T> {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
data: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Retrieves the data from the proxy, if there is no data, it will panic.
|
|
||||||
pub fn take(self) -> T {
|
|
||||||
self.data
|
|
||||||
.expect("the proxy was not provided any data")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Retrieves the data from the proxy, returns `None` if the Proxy has no data.
|
|
||||||
pub fn try_take(self) -> Option<T> {
|
|
||||||
self.data
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: TableProxy> From<T> for Proxy<T> {
|
|
||||||
fn from(value: T) -> Self {
|
|
||||||
Self {
|
|
||||||
data: Some(value),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// should proxying from lua be implicit by implementing this trait for T, instead of Proxy?
|
|
||||||
// it would make it easier, but its more difficult to tell if code is proxying from lua, or
|
|
||||||
// getting a borrow.
|
|
||||||
impl<'a, T: TableProxy> FromLua<'a> for Proxy<T> {
|
|
||||||
fn from_lua(lua: &'a State, val: Value<'a>) -> crate::Result<Self> {
|
|
||||||
let table = val.into_table()?;
|
|
||||||
let t = T::from_table(lua, table)?;
|
|
||||||
|
|
||||||
Ok(Self::from(t))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T: TableProxy> AsLua<'a> for Proxy<T> {
|
|
||||||
fn as_lua(&self, lua: &'a State) -> crate::Result<Value<'a>> {
|
|
||||||
self.data.as_ref()
|
|
||||||
.ok_or(crate::Error::Nil)
|
|
||||||
.and_then(|d| d.as_table(lua))
|
|
||||||
.map(|t| Value::Table(t))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use crate::{tests::Vec2, Function, Proxy, State, StdLibrary, Table, TableProxy};
|
|
||||||
|
|
||||||
impl TableProxy for Vec2 {
|
|
||||||
fn from_table<'a>(_state: &'a crate::State, table: crate::Table<'a>) -> crate::Result<Self> {
|
|
||||||
let x: f32 = table.get("x")?;
|
|
||||||
let y: f32 = table.get("y")?;
|
|
||||||
|
|
||||||
Ok(Vec2 {
|
|
||||||
x,
|
|
||||||
y,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn as_table<'a>(&self, state: &'a crate::State) -> crate::Result<crate::Table<'a>> {
|
|
||||||
let globals = state.globals()?;
|
|
||||||
let vec2: Table = globals.get("Vec2")?;
|
|
||||||
let new_fn: Function = vec2.get("new")?;
|
|
||||||
new_fn.exec((vec2, self.x, self.y))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn table_get() -> crate::Result<()> {
|
|
||||||
let lua = State::new();
|
|
||||||
lua.expose_libraries(&[StdLibrary::Debug, StdLibrary::Package]);
|
|
||||||
|
|
||||||
let res = lua.load(
|
|
||||||
"test.lua",
|
|
||||||
r#"
|
|
||||||
text = "Hello, World"
|
|
||||||
"#)?.execute::<_, ()>(());
|
|
||||||
|
|
||||||
// pretty print the error
|
|
||||||
if let Err(err) = res {
|
|
||||||
panic!("{}", err);
|
|
||||||
}
|
|
||||||
|
|
||||||
let globals = lua.globals()?;
|
|
||||||
let text: String = globals.get("text")?;
|
|
||||||
assert_eq!(text, "Hello, World".to_string());
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn table_set() -> crate::Result<()> {
|
|
||||||
let lua = State::new();
|
|
||||||
lua.expose_libraries(&[StdLibrary::Debug, StdLibrary::Package]);
|
|
||||||
|
|
||||||
let globals = lua.globals()?;
|
|
||||||
globals.set("text", "Hello, World")?;
|
|
||||||
|
|
||||||
let res = lua.load(
|
|
||||||
"test.lua",
|
|
||||||
r#"
|
|
||||||
assert(text == "Hello, World", "The text was not set correctly")
|
|
||||||
"#)?.execute::<_, ()>(());
|
|
||||||
|
|
||||||
// pretty print the error
|
|
||||||
if let Err(err) = res {
|
|
||||||
panic!("{}", err);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn table_proxy() -> crate::Result<()> {
|
|
||||||
let lua = State::new();
|
|
||||||
lua.expose_libraries(&[StdLibrary::Debug, StdLibrary::Package]);
|
|
||||||
|
|
||||||
let res = lua.load(
|
|
||||||
"test.lua",
|
|
||||||
r#"
|
|
||||||
Vec2 = { x = 0.0, y = 0.0 }
|
|
||||||
Vec2.__index = Vec2
|
|
||||||
Vec2.__name = "Vec2"
|
|
||||||
|
|
||||||
function Vec2:new(x, y)
|
|
||||||
local v = {}
|
|
||||||
setmetatable(v, Vec2)
|
|
||||||
|
|
||||||
v.x = x
|
|
||||||
v.y = y
|
|
||||||
|
|
||||||
return v
|
|
||||||
end
|
|
||||||
|
|
||||||
function Vec2:__tostring()
|
|
||||||
return "Vec2(" .. self.x .. ", " .. self.y .. ")"
|
|
||||||
end
|
|
||||||
|
|
||||||
function do_math()
|
|
||||||
return Vec2:new(15, 20)
|
|
||||||
end
|
|
||||||
"#)?.execute::<_, ()>(());
|
|
||||||
|
|
||||||
// pretty print the error
|
|
||||||
if let Err(err) = res {
|
|
||||||
panic!("{}", err);
|
|
||||||
}
|
|
||||||
|
|
||||||
let globals = lua.globals()?;
|
|
||||||
let math_fn: Function = globals.get("do_math")?;
|
|
||||||
let lua_vec2: Table = math_fn.exec(())?;
|
|
||||||
let mut vec2 = Vec2::from_table(&lua, lua_vec2)?;
|
|
||||||
assert_eq!(vec2.x, 15.0);
|
|
||||||
assert_eq!(vec2.y, 20.0);
|
|
||||||
|
|
||||||
vec2.x *= 2.0;
|
|
||||||
vec2.y *= 2.0;
|
|
||||||
|
|
||||||
globals.set("pos", vec2.as_table(&lua)?)?;
|
|
||||||
|
|
||||||
let res = lua.load(
|
|
||||||
"test.lua",
|
|
||||||
r#"
|
|
||||||
-- Vec2 stuff is included from last chunk
|
|
||||||
|
|
||||||
assert(type(pos) == "table", "The global 'pos' is not a table like expected of 'as_table'!!")
|
|
||||||
print("pos is " .. tostring(pos))
|
|
||||||
"#)?.execute::<_, ()>(());
|
|
||||||
|
|
||||||
// pretty print the error
|
|
||||||
if let Err(err) = res {
|
|
||||||
panic!("{}", err);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn table_proxy_value() -> crate::Result<()> {
|
|
||||||
let lua = State::new();
|
|
||||||
lua.expose_libraries(&[StdLibrary::Debug, StdLibrary::Package]);
|
|
||||||
|
|
||||||
let globals = lua.globals()?;
|
|
||||||
let add_fn = lua.create_function(|_, (a, b): (Proxy<Vec2>, Proxy<Vec2>)| {
|
|
||||||
let a = a.take();
|
|
||||||
let b = b.take();
|
|
||||||
|
|
||||||
let x = a.x + b.x;
|
|
||||||
let y = a.y + b.y;
|
|
||||||
|
|
||||||
Ok(Proxy::from(
|
|
||||||
Vec2 {
|
|
||||||
x,
|
|
||||||
y
|
|
||||||
}
|
|
||||||
))
|
|
||||||
})?;
|
|
||||||
globals.set("add_vec2", add_fn)?;
|
|
||||||
|
|
||||||
let res = lua.load(
|
|
||||||
"test.lua",
|
|
||||||
r#"
|
|
||||||
require("util")
|
|
||||||
|
|
||||||
Vec2 = { x = 0.0, y = 0.0 }
|
|
||||||
Vec2.__index = Vec2
|
|
||||||
Vec2.__name = "Vec2"
|
|
||||||
|
|
||||||
function Vec2:new(x, y)
|
|
||||||
local v = {}
|
|
||||||
setmetatable(v, Vec2)
|
|
||||||
|
|
||||||
v.x = x
|
|
||||||
v.y = y
|
|
||||||
|
|
||||||
return v
|
|
||||||
end
|
|
||||||
|
|
||||||
function Vec2:__tostring()
|
|
||||||
return "Vec2(" .. self.x .. ", " .. self.y .. ")"
|
|
||||||
end
|
|
||||||
|
|
||||||
function do_math()
|
|
||||||
return Vec2:new(15, 20)
|
|
||||||
end
|
|
||||||
|
|
||||||
local v1 = Vec2:new(15, 20)
|
|
||||||
local v2 = Vec2:new(7, 10)
|
|
||||||
local v3 = add_vec2(v1, v2)
|
|
||||||
|
|
||||||
assert(v3.x == 22 and v3.y == 30, "The result from adding the values was incorrect!")
|
|
||||||
|
|
||||||
--print("v3 is " .. dump_table(v3))
|
|
||||||
--print("Added together, v3 is " .. tostring(v3))
|
|
||||||
"#)?.execute::<_, ()>(());
|
|
||||||
|
|
||||||
// pretty print the error
|
|
||||||
if let Err(err) = res {
|
|
||||||
panic!("{}", err);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
62
src/tests.rs
62
src/tests.rs
|
@ -1,62 +0,0 @@
|
||||||
use std::cell::Ref;
|
|
||||||
|
|
||||||
use crate::{MetaMethod, State, Userdata, UserdataBuilder};
|
|
||||||
|
|
||||||
pub struct Vec2 {
|
|
||||||
pub x: f32,
|
|
||||||
pub y: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Userdata for Vec2 {
|
|
||||||
fn build<'a>(_state: &State, builder: &mut UserdataBuilder<'a, Vec2>) -> crate::Result<()> {
|
|
||||||
builder
|
|
||||||
.field_getter("x", |_, this| Ok(this.x))
|
|
||||||
.field_getter("y", |_, this| Ok(this.y))
|
|
||||||
.field_setter("x", |_, this, x: f32| this.x = x)
|
|
||||||
.field_setter("y", |_, this, y: f32| this.y = y)
|
|
||||||
.function("new", |lua, (x, y)| lua.create_userdata(Vec2 { x, y }))
|
|
||||||
// method test
|
|
||||||
.method("add", |lua, lhs: &Vec2, (rhs,): (Ref<Vec2>,)| {
|
|
||||||
let lx = lhs.x;
|
|
||||||
let ly = lhs.y;
|
|
||||||
|
|
||||||
let rx = rhs.x;
|
|
||||||
let ry = rhs.y;
|
|
||||||
|
|
||||||
lua.create_userdata(Vec2 {
|
|
||||||
x: lx + rx,
|
|
||||||
y: ly + ry,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.method_mut("mult", |_, this: &mut Vec2, scalar: f32| {
|
|
||||||
this.x *= scalar;
|
|
||||||
this.y *= scalar;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
})
|
|
||||||
.method("mult_ret", |lua, this: &Vec2, scalar: f32| {
|
|
||||||
lua.create_userdata(Vec2 {
|
|
||||||
x: this.x * scalar,
|
|
||||||
y: this.y * scalar,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.meta_method(MetaMethod::Add, |lua, lhs: &Vec2, (rhs,): (Ref<Vec2>,)| {
|
|
||||||
let lx = lhs.x;
|
|
||||||
let ly = lhs.y;
|
|
||||||
|
|
||||||
let rx = rhs.x;
|
|
||||||
let ry = rhs.y;
|
|
||||||
|
|
||||||
lua.create_userdata(Vec2 {
|
|
||||||
x: lx + rx,
|
|
||||||
y: ly + ry,
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn name() -> String {
|
|
||||||
"Vec2".to_string()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,24 +1,11 @@
|
||||||
use std::{cell::{OnceCell, Ref, RefCell, RefMut}, collections::HashMap, ffi::CStr, marker::PhantomData, mem, ops::DerefMut, sync::Arc};
|
use std::{borrow::Borrow, cell::{Ref, RefCell, RefMut}, collections::HashMap, ffi::CStr, marker::PhantomData, ops::{Deref, DerefMut}};
|
||||||
|
|
||||||
use crate::{ensure_type, AsLua, FromLua, FromLuaStack, FromLuaVec, Function, IntoLuaVec, LuaRef, PushToLuaStack, StackGuard, State, Table, Value, ValueVec};
|
use crate::{ensure_type, AsLua, FromLua, FromLuaStack, FromLuaVec, LuaRef, PushToLuaStack, StackGuard, State, Value, ValueVec};
|
||||||
|
|
||||||
use mlua_sys as lua;
|
use mlua_sys as lua;
|
||||||
|
|
||||||
pub mod unsafe_ud;
|
//pub type FieldSetter<T> = fn(lua: &State, this: &T);
|
||||||
pub use unsafe_ud::*;
|
//pub type FieldGetter<T, U> = fn(lua: &State, this: &T, val: &U);
|
||||||
|
|
||||||
pub mod proxy;
|
|
||||||
#[allow(unused_imports)]
|
|
||||||
use proxy::*;
|
|
||||||
|
|
||||||
pub mod borrow;
|
|
||||||
#[allow(unused_imports)]
|
|
||||||
use borrow::*;
|
|
||||||
|
|
||||||
pub mod borrow_mut;
|
|
||||||
#[allow(unused_imports)]
|
|
||||||
use borrow_mut::*;
|
|
||||||
|
|
||||||
|
|
||||||
/// An enum representing all Lua MetaMethods
|
/// An enum representing all Lua MetaMethods
|
||||||
/// https://gist.github.com/oatmealine/655c9e64599d0f0dd47687c1186de99f
|
/// https://gist.github.com/oatmealine/655c9e64599d0f0dd47687c1186de99f
|
||||||
|
@ -105,9 +92,15 @@ impl<'a> PushToLuaStack<'a> for MetaMethod {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type UserdataFn<'a> = Box<dyn Fn(&'a State, ValueVec<'a>) -> crate::Result<Value<'a>>>;
|
pub trait FieldSetter {
|
||||||
pub type UserdataGetterFn<'a> = Arc<OnceCell<Box<dyn Fn(AnyUserdata<'a>) -> crate::Result<*const ()>>>>;
|
fn set_field(&self, val: Value);
|
||||||
pub type UserdataMutGetterFn<'a> = Arc<OnceCell<Box<dyn Fn(AnyUserdata<'a>) -> crate::Result<*mut ()>>>>;
|
}
|
||||||
|
|
||||||
|
pub trait FieldGetter {
|
||||||
|
fn get_field(&self) -> Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
type UserdataFn<'a> = Box<dyn Fn(&'a State, ValueVec<'a>) -> crate::Result<Value<'a>>>;
|
||||||
|
|
||||||
pub struct UserdataBuilder<'a, T> {
|
pub struct UserdataBuilder<'a, T> {
|
||||||
pub(crate) name: String,
|
pub(crate) name: String,
|
||||||
|
@ -115,10 +108,6 @@ pub struct UserdataBuilder<'a, T> {
|
||||||
pub(crate) field_setters: HashMap<String, UserdataFn<'a>>,
|
pub(crate) field_setters: HashMap<String, UserdataFn<'a>>,
|
||||||
pub(crate) functions: HashMap<String, UserdataFn<'a>>,
|
pub(crate) functions: HashMap<String, UserdataFn<'a>>,
|
||||||
pub(crate) meta_methods: HashMap<String, UserdataFn<'a>>,
|
pub(crate) meta_methods: HashMap<String, UserdataFn<'a>>,
|
||||||
|
|
||||||
pub(crate) wrapped_getter: Arc<OnceCell<Box<dyn Fn(AnyUserdata<'a>) -> crate::Result<*const ()>>>>,
|
|
||||||
pub(crate) wrapped_getter_mut: Arc<OnceCell<Box<dyn Fn(AnyUserdata<'a>) -> crate::Result<*mut ()>>>>,
|
|
||||||
|
|
||||||
_marker: PhantomData<T>,
|
_marker: PhantomData<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,8 +119,6 @@ impl<'a, T: Userdata> UserdataBuilder<'a, T> {
|
||||||
field_setters: HashMap::new(),
|
field_setters: HashMap::new(),
|
||||||
functions: HashMap::new(),
|
functions: HashMap::new(),
|
||||||
meta_methods: HashMap::new(),
|
meta_methods: HashMap::new(),
|
||||||
wrapped_getter: Arc::new(OnceCell::new()),
|
|
||||||
wrapped_getter_mut: Arc::new(OnceCell::new()),
|
|
||||||
_marker: PhantomData,
|
_marker: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -142,32 +129,11 @@ impl<'a, T: Userdata> UserdataBuilder<'a, T> {
|
||||||
R: AsLua<'a>,
|
R: AsLua<'a>,
|
||||||
T: Userdata + 'static
|
T: Userdata + 'static
|
||||||
{
|
{
|
||||||
let wrapped = self.wrapped_getter.clone();
|
|
||||||
let wrapped: Arc<OnceCell<Box<dyn Fn(AnyUserdata<'_>) -> Result<*const (), crate::Error>>>> = unsafe { mem::transmute(wrapped) };
|
|
||||||
|
|
||||||
let ud_name = self.name.clone();
|
|
||||||
let fn_name = name.to_string();
|
|
||||||
|
|
||||||
let wrap = move |lua: &'a State, mut val: ValueVec<'a>| {
|
let wrap = move |lua: &'a State, mut val: ValueVec<'a>| {
|
||||||
let val = val.pop_front().unwrap();
|
let val = val.pop_front().unwrap();
|
||||||
let this = val.as_userdata().unwrap(); // if this panics, its a bug
|
let this = val.as_userdata().unwrap(); // if this panics, its a bug
|
||||||
|
let this = this.as_ref::<T>()?;
|
||||||
if let Some(getter) = wrapped.get() {
|
f(lua, &*this).and_then(|r| r.as_lua(lua))
|
||||||
let this_ptr = Self::result_to_bad_arg(
|
|
||||||
getter(this.clone()),
|
|
||||||
&ud_name,
|
|
||||||
&fn_name,
|
|
||||||
1,
|
|
||||||
Some("self")
|
|
||||||
)?;
|
|
||||||
let this_ptr = this_ptr.cast::<T>();
|
|
||||||
let this = unsafe { &*this_ptr };
|
|
||||||
|
|
||||||
f(lua, this).and_then(|r| r.as_lua(lua))
|
|
||||||
} else {
|
|
||||||
let this = this.as_ref::<T>()?;
|
|
||||||
f(lua, &*this).and_then(|r| r.as_lua(lua))
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
self.field_getters.insert(name.to_string(), Box::new(wrap));
|
self.field_getters.insert(name.to_string(), Box::new(wrap));
|
||||||
|
|
||||||
|
@ -180,43 +146,22 @@ impl<'a, T: Userdata> UserdataBuilder<'a, T> {
|
||||||
V: FromLua<'a>,
|
V: FromLua<'a>,
|
||||||
T: Userdata + 'static
|
T: Userdata + 'static
|
||||||
{
|
{
|
||||||
let wrapped = self.wrapped_getter_mut.clone();
|
|
||||||
let wrapped: Arc<OnceCell<Box<dyn Fn(AnyUserdata<'_>) -> Result<*mut (), crate::Error>>>> = unsafe { mem::transmute(wrapped) };
|
|
||||||
|
|
||||||
let ud_name = self.name.clone();
|
|
||||||
let fn_name = name.to_string();
|
|
||||||
|
|
||||||
let wrap = move |lua: &'a State, mut val: ValueVec<'a>| {
|
let wrap = move |lua: &'a State, mut val: ValueVec<'a>| {
|
||||||
let lua_val = val.pop_front().unwrap();
|
let lua_val = val.pop_front().unwrap();
|
||||||
let this = lua_val.as_userdata().unwrap(); // if this panics, its a bug
|
let this = lua_val.as_userdata().unwrap(); // if this panics, its a bug
|
||||||
|
let mut this = this.as_mut::<T>()?;
|
||||||
|
|
||||||
let lua_val = val.pop_front().unwrap();
|
let lua_val = val.pop_front().unwrap();
|
||||||
let v_arg = V::from_lua(lua, lua_val)?;
|
let v_arg = V::from_lua(lua, lua_val)?;
|
||||||
|
|
||||||
if let Some(mut_getter) = wrapped.get() {
|
f(lua, this.deref_mut(), v_arg).as_lua(lua)
|
||||||
let this_ptr = Self::result_to_bad_arg(
|
|
||||||
mut_getter(this.clone()),
|
|
||||||
&ud_name,
|
|
||||||
&fn_name,
|
|
||||||
1,
|
|
||||||
Some("self")
|
|
||||||
)?;
|
|
||||||
let this_ptr = this_ptr.cast::<T>();
|
|
||||||
let this = unsafe { this_ptr.as_mut().unwrap() };
|
|
||||||
|
|
||||||
f(lua, this, v_arg).as_lua(lua)
|
|
||||||
} else {
|
|
||||||
let mut this = this.as_mut::<T>()?;
|
|
||||||
|
|
||||||
f(lua, this.deref_mut(), v_arg).as_lua(lua)
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
self.field_setters.insert(name.to_string(), Box::new(wrap));
|
self.field_setters.insert(name.to_string(), Box::new(wrap));
|
||||||
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
fn arg_result_to_bad_arg<R>(res: crate::Result<R>, ud_name: &str, fn_name: &str) -> crate::Result<R> {
|
fn result_to_bad_arg<R>(res: crate::Result<R>, ud_name: &str, fn_name: &str) -> crate::Result<R> {
|
||||||
res.map_err(|e| match e {
|
res.map_err(|e| match e {
|
||||||
crate::Error::ValueVecError { value_idx, error } => {
|
crate::Error::ValueVecError { value_idx, error } => {
|
||||||
let full_name = format!("{}.{}", ud_name, fn_name);
|
let full_name = format!("{}.{}", ud_name, fn_name);
|
||||||
|
@ -228,27 +173,17 @@ impl<'a, T: Userdata> UserdataBuilder<'a, T> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn result_to_bad_arg<R>(res: crate::Result<R>, ud_name: &str, fn_name: &str, arg_idx: i32, arg_name: Option<&str>) -> crate::Result<R> {
|
|
||||||
res.map_err(|e| {
|
|
||||||
let full_name = format!("{}.{}", ud_name, fn_name);
|
|
||||||
let arg_name = arg_name.map(|s| s.to_string());
|
|
||||||
|
|
||||||
crate::Error::BadArgument { func: Some(full_name), arg_index: arg_idx, arg_name, error: Arc::new(e), }
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn function<F, R, A>(&mut self, name: &str, f: F) -> &mut Self
|
pub fn function<F, R, A>(&mut self, name: &str, f: F) -> &mut Self
|
||||||
where
|
where
|
||||||
F: Fn(&'a State, A) -> crate::Result<R> + 'static,
|
F: Fn(&'a State, A) -> crate::Result<R> + 'static,
|
||||||
A: FromLuaVec<'a>,
|
A: FromLuaVec<'a>,
|
||||||
R: AsLua<'a>,
|
R: AsLua<'a>,
|
||||||
{
|
{
|
||||||
let ud_name = self.name.clone();
|
|
||||||
let fn_name = name.to_string();
|
let fn_name = name.to_string();
|
||||||
let wrap = move |lua: &'a State, val: ValueVec<'a>| {
|
let wrap = move |lua: &'a State, val: ValueVec<'a>| {
|
||||||
let args = Self::arg_result_to_bad_arg(
|
let args = Self::result_to_bad_arg(
|
||||||
A::from_lua_value_vec(lua, val),
|
A::from_lua_value_vec(lua, val),
|
||||||
&ud_name, &fn_name
|
&T::name(), &fn_name
|
||||||
)?;
|
)?;
|
||||||
f(lua, args).and_then(|r| r.as_lua(lua))
|
f(lua, args).and_then(|r| r.as_lua(lua))
|
||||||
};
|
};
|
||||||
|
@ -264,83 +199,19 @@ impl<'a, T: Userdata> UserdataBuilder<'a, T> {
|
||||||
R: AsLua<'a>,
|
R: AsLua<'a>,
|
||||||
T: Userdata + 'static
|
T: Userdata + 'static
|
||||||
{
|
{
|
||||||
let wrapped = self.wrapped_getter.clone();
|
|
||||||
let wrapped: Arc<OnceCell<Box<dyn Fn(AnyUserdata<'_>) -> Result<*const (), crate::Error>>>> = unsafe { mem::transmute(wrapped) };
|
|
||||||
|
|
||||||
let ud_name = self.name.clone();
|
|
||||||
let fn_name = name.to_string();
|
let fn_name = name.to_string();
|
||||||
|
|
||||||
let wrap = move |lua: &'a State, mut val: ValueVec<'a>| {
|
let wrap = move |lua: &'a State, mut val: ValueVec<'a>| {
|
||||||
let this_val = val.pop_front().unwrap();
|
let this_val = val.pop_front().unwrap();
|
||||||
let this = this_val.as_userdata().unwrap(); // if this panics, its a bug
|
let this = this_val.as_userdata().unwrap(); // if this panics, its a bug
|
||||||
|
let this = this.as_ref::<T>()?;
|
||||||
let args = Self::arg_result_to_bad_arg(
|
|
||||||
|
let this_name = T::name();
|
||||||
|
let args = Self::result_to_bad_arg(
|
||||||
A::from_lua_value_vec(lua, val),
|
A::from_lua_value_vec(lua, val),
|
||||||
&ud_name, &fn_name
|
&this_name, &fn_name
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
if let Some(mut_getter) = wrapped.get() {
|
f(lua, &*this, args).and_then(|r| r.as_lua(lua))
|
||||||
let this_ptr = Self::result_to_bad_arg(
|
|
||||||
mut_getter(this.clone()),
|
|
||||||
&ud_name,
|
|
||||||
&fn_name,
|
|
||||||
1,
|
|
||||||
Some("self")
|
|
||||||
)?;
|
|
||||||
let this_ptr = this_ptr.cast::<T>();
|
|
||||||
let this = unsafe { this_ptr.as_ref().unwrap() };
|
|
||||||
|
|
||||||
f(lua, this, args).and_then(|r| r.as_lua(lua))
|
|
||||||
} else {
|
|
||||||
let this = this.as_ref::<T>()?;
|
|
||||||
|
|
||||||
f(lua, &*this, args).and_then(|r| r.as_lua(lua))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
self.functions.insert(name.to_string(), Box::new(wrap));
|
|
||||||
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn method_mut<F, R, A>(&mut self, name: &str, f: F) -> &mut Self
|
|
||||||
where
|
|
||||||
F: Fn(&'a State, &mut T, A) -> crate::Result<R> + 'static,
|
|
||||||
A: FromLuaVec<'a>,
|
|
||||||
R: AsLua<'a>,
|
|
||||||
T: Userdata + 'static
|
|
||||||
{
|
|
||||||
let wrapped = self.wrapped_getter_mut.clone();
|
|
||||||
let wrapped: Arc<OnceCell<Box<dyn Fn(AnyUserdata<'_>) -> Result<*mut (), crate::Error>>>> = unsafe { mem::transmute(wrapped) };
|
|
||||||
|
|
||||||
let ud_name = self.name.clone();
|
|
||||||
let fn_name = name.to_string();
|
|
||||||
|
|
||||||
let wrap = move |lua: &'a State, mut val: ValueVec<'a>| {
|
|
||||||
let this_val = val.pop_front().unwrap();
|
|
||||||
let this = this_val.as_userdata().unwrap(); // if this panics, its a bug
|
|
||||||
|
|
||||||
let args = Self::arg_result_to_bad_arg(
|
|
||||||
A::from_lua_value_vec(lua, val),
|
|
||||||
&ud_name, &fn_name
|
|
||||||
)?;
|
|
||||||
|
|
||||||
if let Some(mut_getter) = wrapped.get() {
|
|
||||||
let this_ptr = Self::result_to_bad_arg(
|
|
||||||
mut_getter(this.clone()),
|
|
||||||
&ud_name,
|
|
||||||
&fn_name,
|
|
||||||
1,
|
|
||||||
Some("self")
|
|
||||||
)?;
|
|
||||||
let this_ptr = this_ptr.cast::<T>();
|
|
||||||
let this = unsafe { this_ptr.as_mut().unwrap() };
|
|
||||||
|
|
||||||
f(lua, this, args).and_then(|r| r.as_lua(lua))
|
|
||||||
} else {
|
|
||||||
let mut this = this.as_mut::<T>()?;
|
|
||||||
|
|
||||||
f(lua, this.deref_mut(), args).and_then(|r| r.as_lua(lua))
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
self.functions.insert(name.to_string(), Box::new(wrap));
|
self.functions.insert(name.to_string(), Box::new(wrap));
|
||||||
|
|
||||||
|
@ -355,16 +226,16 @@ impl<'a, T: Userdata> UserdataBuilder<'a, T> {
|
||||||
R: AsLua<'a>,
|
R: AsLua<'a>,
|
||||||
T: Userdata + 'static
|
T: Userdata + 'static
|
||||||
{
|
{
|
||||||
let ud_name = self.name.clone();
|
|
||||||
let fn_name = name.as_ref().to_string();
|
let fn_name = name.as_ref().to_string();
|
||||||
let wrap = move |lua: &'a State, mut val: ValueVec<'a>| {
|
let wrap = move |lua: &'a State, mut val: ValueVec<'a>| {
|
||||||
let this_val = val.pop_front().unwrap();
|
let this_val = val.pop_front().unwrap();
|
||||||
let this = this_val.as_userdata().unwrap(); // if this panics, its a bug
|
let this = this_val.as_userdata().unwrap(); // if this panics, its a bug
|
||||||
let this = this.as_ref::<T>()?;
|
let this = this.as_ref::<T>()?;
|
||||||
|
|
||||||
let args = Self::arg_result_to_bad_arg(
|
let this_name = T::name();
|
||||||
|
let args = Self::result_to_bad_arg(
|
||||||
A::from_lua_value_vec(lua, val),
|
A::from_lua_value_vec(lua, val),
|
||||||
&ud_name, &fn_name
|
&this_name, &fn_name
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
f(lua, &*this, args).and_then(|r| r.as_lua(lua))
|
f(lua, &*this, args).and_then(|r| r.as_lua(lua))
|
||||||
|
@ -373,61 +244,78 @@ impl<'a, T: Userdata> UserdataBuilder<'a, T> {
|
||||||
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
//fn append_fields_from<S>(&mut self, other: UserDataRegistry<'lua, S>) {
|
|
||||||
pub fn expand_with<U>(&mut self, other: UserdataBuilder<'a, U>) {
|
|
||||||
self.field_getters = other.field_getters;
|
|
||||||
self.field_setters = other.field_setters;
|
|
||||||
self.functions = other.functions;
|
|
||||||
self.meta_methods = other.meta_methods;
|
|
||||||
self.wrapped_getter = other.wrapped_getter;
|
|
||||||
self.wrapped_getter_mut = other.wrapped_getter_mut;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Userdata: Sized {
|
pub trait Userdata: Sized {
|
||||||
fn name() -> String;
|
fn name() -> String;
|
||||||
|
|
||||||
fn build<'a>(state: &State, builder: &mut UserdataBuilder<'a, Self>) -> crate::Result<()>;
|
fn build<'a>(builder: &mut UserdataBuilder<'a, Self>) -> crate::Result<()>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A handle to some userdata on the stack
|
/// A handle to some userdata on the stack
|
||||||
//#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct AnyUserdata<'a> {
|
pub struct AnyUserdata<'a> {
|
||||||
pub(crate) unsafe_ud: UnsafeUserdata<'a>,
|
pub(crate) lref: LuaRef<'a>,
|
||||||
state: &'a State,
|
state: &'a State,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Clone for AnyUserdata<'a> {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
Self {
|
|
||||||
state: self.state,
|
|
||||||
unsafe_ud: self.unsafe_ud.clone()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> AnyUserdata<'a> {
|
impl<'a> AnyUserdata<'a> {
|
||||||
pub fn from_ref(state: &'a State, lref: LuaRef<'a>) -> Self {
|
pub fn from_ref(state: &'a State, lref: LuaRef<'a>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
lref,
|
||||||
state,
|
state,
|
||||||
unsafe_ud: UnsafeUserdata::from_ref(state, lref)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a borrow to the userdata.
|
/// Returns a borrow to the userdata.
|
||||||
pub fn as_ref<T: Userdata + 'static>(&self) -> crate::Result<Ref<'a, T>> {
|
pub fn as_ref<T: Userdata + 'static>(&self) -> crate::Result<Ref<'a, T>> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let cell = &*self.unsafe_ud.as_ptr_unchecked::<RefCell<T>>()?;
|
self.state.ensure_stack(3)?;
|
||||||
Ok(cell.borrow())
|
let _g = StackGuard::new(self.state);
|
||||||
|
let s = self.state.state_ptr();
|
||||||
|
|
||||||
|
self.lref.push_to_lua_stack(self.state)?;
|
||||||
|
|
||||||
|
if lua::lua_getmetatable(s, -1) == 0 {
|
||||||
|
return Err(crate::Error::UserdataMismatch);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.state.get_userdata_metatable::<T>()
|
||||||
|
.push_to_lua_stack(self.state)?;
|
||||||
|
|
||||||
|
if lua::lua_rawequal(s, -2, -1) == 1 {
|
||||||
|
let cptr = lua::lua_touserdata(s, -3);
|
||||||
|
let cell = &*cptr.cast::<RefCell<T>>();
|
||||||
|
Ok(cell.borrow())
|
||||||
|
} else {
|
||||||
|
return Err(crate::Error::UserdataMismatch);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a mutable reference to the userdata.
|
/// Returns a mutable reference to the userdata.
|
||||||
pub fn as_mut<T: Userdata + 'static>(&self) -> crate::Result<RefMut<'a, T>> {
|
pub fn as_mut<T: Userdata + 'static>(&self) -> crate::Result<RefMut<'a, T>> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let cell = &*self.unsafe_ud.as_ptr_unchecked::<RefCell<T>>()?;
|
self.state.ensure_stack(3)?;
|
||||||
Ok(cell.borrow_mut())
|
let _g = StackGuard::new(self.state);
|
||||||
|
let s = self.state.state_ptr();
|
||||||
|
|
||||||
|
self.lref.push_to_lua_stack(self.state)?;
|
||||||
|
|
||||||
|
if lua::lua_getmetatable(s, -1) == 0 {
|
||||||
|
return Err(crate::Error::MissingMetatable);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.state.get_userdata_metatable::<T>()
|
||||||
|
.push_to_lua_stack(self.state)?;
|
||||||
|
|
||||||
|
if lua::lua_rawequal(s, -2, -1) == 1 {
|
||||||
|
let cptr = lua::lua_touserdata(s, -3);
|
||||||
|
let cell = &*cptr.cast::<RefCell<T>>();
|
||||||
|
Ok(cell.borrow_mut())
|
||||||
|
} else {
|
||||||
|
Err(crate::Error::UserdataMismatch)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -440,8 +328,14 @@ impl<'a> AnyUserdata<'a> {
|
||||||
/// If there is a possibility that these types do not match, use [`AnyUserdata::as_ptr`]
|
/// If there is a possibility that these types do not match, use [`AnyUserdata::as_ptr`]
|
||||||
/// which does verify the types.
|
/// which does verify the types.
|
||||||
pub unsafe fn as_ptr_unchecked<T: Userdata + 'static>(&self) -> crate::Result<*mut RefCell<T>> {
|
pub unsafe fn as_ptr_unchecked<T: Userdata + 'static>(&self) -> crate::Result<*mut RefCell<T>> {
|
||||||
let cell = self.unsafe_ud.as_ptr_unchecked::<RefCell<T>>()?;
|
self.state.ensure_stack(1)?;
|
||||||
Ok(cell)
|
let _g = StackGuard::new(self.state);
|
||||||
|
let s = self.state.state_ptr();
|
||||||
|
|
||||||
|
self.lref.push_to_lua_stack(self.state)?;
|
||||||
|
let cptr = lua::lua_touserdata(s, -3);
|
||||||
|
|
||||||
|
Ok(cptr.cast())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a mutable pointer of the [`RefCell`] storing the userdata.
|
/// Returns a mutable pointer of the [`RefCell`] storing the userdata.
|
||||||
|
@ -452,7 +346,7 @@ impl<'a> AnyUserdata<'a> {
|
||||||
let _g = StackGuard::new(self.state);
|
let _g = StackGuard::new(self.state);
|
||||||
let s = self.state.state_ptr();
|
let s = self.state.state_ptr();
|
||||||
|
|
||||||
self.unsafe_ud.lref.push_to_lua_stack(self.state)?;
|
self.lref.push_to_lua_stack(self.state)?;
|
||||||
|
|
||||||
if lua::lua_getmetatable(s, -1) == 0 {
|
if lua::lua_getmetatable(s, -1) == 0 {
|
||||||
return Err(crate::Error::MissingMetatable);
|
return Err(crate::Error::MissingMetatable);
|
||||||
|
@ -462,9 +356,8 @@ impl<'a> AnyUserdata<'a> {
|
||||||
.push_to_lua_stack(self.state)?;
|
.push_to_lua_stack(self.state)?;
|
||||||
|
|
||||||
if lua::lua_rawequal(s, -2, -1) == 1 {
|
if lua::lua_rawequal(s, -2, -1) == 1 {
|
||||||
drop(_g);
|
let cptr = lua::lua_touserdata(s, -3);
|
||||||
let cell = self.unsafe_ud.as_ptr_unchecked::<RefCell<T>>()?;
|
Ok(cptr.cast())
|
||||||
Ok(cell)
|
|
||||||
} else {
|
} else {
|
||||||
Err(crate::Error::UserdataMismatch)
|
Err(crate::Error::UserdataMismatch)
|
||||||
}
|
}
|
||||||
|
@ -476,7 +369,7 @@ impl<'a> AnyUserdata<'a> {
|
||||||
self.state.ensure_stack(3)?;
|
self.state.ensure_stack(3)?;
|
||||||
let _g = StackGuard::new(self.state);
|
let _g = StackGuard::new(self.state);
|
||||||
|
|
||||||
self.unsafe_ud.lref.push_to_lua_stack(self.state)?;
|
self.lref.push_to_lua_stack(self.state)?;
|
||||||
let s = self.state.state_ptr();
|
let s = self.state.state_ptr();
|
||||||
|
|
||||||
if lua::lua_getmetatable(s, -1) == 0 {
|
if lua::lua_getmetatable(s, -1) == 0 {
|
||||||
|
@ -496,67 +389,6 @@ impl<'a> AnyUserdata<'a> {
|
||||||
Ok(cstr.to_string())
|
Ok(cstr.to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the metatable of this userdata
|
|
||||||
pub fn get_metatable(&'a self) -> crate::Result<Table<'a>> {
|
|
||||||
unsafe {
|
|
||||||
self.state.ensure_stack(2)?;
|
|
||||||
let _g = StackGuard::new(self.state);
|
|
||||||
|
|
||||||
//self.unsafe_ud.lref.push_to_lua_stack(self.state)?;
|
|
||||||
self.push_to_lua_stack(self.state)?;
|
|
||||||
let s = self.state.state_ptr();
|
|
||||||
|
|
||||||
if lua::lua_getmetatable(s, -1) == 0 {
|
|
||||||
Err(crate::Error::MissingMetatable)
|
|
||||||
} else {
|
|
||||||
let mut t = Table::from_lua_stack(self.state)?;
|
|
||||||
t.mt_marker = true;
|
|
||||||
|
|
||||||
Ok(t)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets something from the userdata.
|
|
||||||
///
|
|
||||||
/// Will trigger a call to the `__index` metamethod. Use [`AnyUserdata::raw_get`] if you
|
|
||||||
/// don't want to call it.
|
|
||||||
pub fn get<K, V>(&'a self, key: K) -> crate::Result<V>
|
|
||||||
where
|
|
||||||
K: AsLua<'a>,
|
|
||||||
V: FromLua<'a>,
|
|
||||||
{
|
|
||||||
let mt = self.get_metatable()?;
|
|
||||||
mt.get::<K, V>(key)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets something from the userdata. Will **not** trigger a call to the `__index` metamethod.
|
|
||||||
pub fn raw_get<K, V>(&'a self, key: K) -> crate::Result<V>
|
|
||||||
where
|
|
||||||
K: AsLua<'a>,
|
|
||||||
V: FromLua<'a>,
|
|
||||||
{
|
|
||||||
let mt = self.get_metatable()?;
|
|
||||||
mt.raw_get::<K, V>(key)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Execute a method on this userdata. This finds a function with `name` and executes it
|
|
||||||
/// with the userdata as the first argument.
|
|
||||||
pub fn execute_method<A, R>(&'a self, name: &str, args: A) -> crate::Result<R>
|
|
||||||
where
|
|
||||||
A: IntoLuaVec<'a>,
|
|
||||||
R: FromLua<'a>,
|
|
||||||
{
|
|
||||||
let name = name.to_string();
|
|
||||||
let mt = self.get_metatable()?;
|
|
||||||
let f = mt.get::<_, Function>(name)?;
|
|
||||||
|
|
||||||
let mut args = args.into_lua_value_vec(self.state)?;
|
|
||||||
args.push_front(self.clone().as_lua(self.state)?);
|
|
||||||
|
|
||||||
f.exec(args)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> FromLuaStack<'a> for AnyUserdata<'a> {
|
impl<'a> FromLuaStack<'a> for AnyUserdata<'a> {
|
||||||
|
@ -564,15 +396,15 @@ impl<'a> FromLuaStack<'a> for AnyUserdata<'a> {
|
||||||
ensure_type(state, lua::LUA_TUSERDATA, -1)?;
|
ensure_type(state, lua::LUA_TUSERDATA, -1)?;
|
||||||
|
|
||||||
Ok(AnyUserdata {
|
Ok(AnyUserdata {
|
||||||
|
lref: LuaRef::from_stack(state)?,
|
||||||
state,
|
state,
|
||||||
unsafe_ud: UnsafeUserdata::from_lua_stack(state)?,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> PushToLuaStack<'a> for AnyUserdata<'a> {
|
impl<'a> PushToLuaStack<'a> for AnyUserdata<'a> {
|
||||||
unsafe fn push_to_lua_stack(&self, state: &'a State) -> crate::Result<()> {
|
unsafe fn push_to_lua_stack(&self, state: &'a State) -> crate::Result<()> {
|
||||||
self.unsafe_ud.lref.push_to_lua_stack(state)
|
self.lref.push_to_lua_stack(state)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,168 +0,0 @@
|
||||||
use std::{cell::Ref, mem, ops::Deref};
|
|
||||||
|
|
||||||
use crate::{AnyUserdata, Error, Result, State, Userdata, UserdataBuilder};
|
|
||||||
|
|
||||||
enum Borrow<'a, T> {
|
|
||||||
Wrapped(Ref<'a, T>),
|
|
||||||
Raw(&'a T),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T> Deref for Borrow<'a, T> {
|
|
||||||
type Target = T;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
match self {
|
|
||||||
Borrow::Wrapped(w) => w,
|
|
||||||
Borrow::Raw(w) => w,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct UserdataRef<'a, T: Userdata> {
|
|
||||||
borrow: Borrow<'a, T>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T: Userdata> From<&'a T> for UserdataRef<'static, T> {
|
|
||||||
fn from(value: &'a T) -> Self {
|
|
||||||
let ud = Borrow::Raw(value);
|
|
||||||
|
|
||||||
Self {
|
|
||||||
borrow: unsafe {
|
|
||||||
mem::transmute(ud)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T: Userdata> From<Ref<'a, T>> for UserdataRef<'static, T> {
|
|
||||||
fn from(value: Ref<'a, T>) -> Self {
|
|
||||||
let ud = Borrow::Wrapped(value);
|
|
||||||
|
|
||||||
Self {
|
|
||||||
borrow: unsafe {
|
|
||||||
mem::transmute(ud)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T: Userdata + 'static> Userdata for UserdataRef<'a, T> {
|
|
||||||
fn build<'b>(state: &State, builder: &mut UserdataBuilder<'b, Self>) -> crate::Result<()> {
|
|
||||||
let mut other = UserdataBuilder::<T>::new();
|
|
||||||
T::build(state, &mut other)?;
|
|
||||||
|
|
||||||
builder.expand_with(other);
|
|
||||||
|
|
||||||
let getter: fn(AnyUserdata<'_>) -> Result<*const ()> = move |ud: AnyUserdata| {
|
|
||||||
let ud_ptr = {
|
|
||||||
let ud = ud.as_ref::<UserdataRef<T>>()?;
|
|
||||||
|
|
||||||
let ud_ptr: *const T = &*ud.borrow;
|
|
||||||
ud_ptr
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(ud_ptr.cast::<()>())
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut_getter: fn(AnyUserdata<'_>) -> Result<*mut ()> = move |_ud: AnyUserdata| {
|
|
||||||
Err(Error::Runtime(format!("cannot mutably access '{}' when its behind a non mutable reference!", T::name())))
|
|
||||||
};
|
|
||||||
|
|
||||||
if builder.wrapped_getter.set(Box::new(getter)).is_err() {
|
|
||||||
panic!("Somehow the wrapped getter has already been set");
|
|
||||||
}
|
|
||||||
|
|
||||||
if builder.wrapped_getter_mut.set(Box::new(mut_getter)).is_err() {
|
|
||||||
panic!("Somehow the wrapped mutable getter has already been set");
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn name() -> String {
|
|
||||||
let name = format!("{}Ref", T::name());
|
|
||||||
name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use std::cell::{Ref, RefCell};
|
|
||||||
|
|
||||||
use crate::{tests::Vec2, State, StdLibrary, Value};
|
|
||||||
|
|
||||||
use super::UserdataRef;
|
|
||||||
|
|
||||||
/// This test ensures that a Ref of userdata can be provided to Lua, and it that it can access fields on the userdata.
|
|
||||||
#[test]
|
|
||||||
fn ud_fields_borrow() -> crate::Result<()> {
|
|
||||||
let lua = State::new();
|
|
||||||
lua.expose_libraries(&[StdLibrary::Debug, StdLibrary::Package]);
|
|
||||||
|
|
||||||
let globals = lua.globals()?;
|
|
||||||
|
|
||||||
let v1 = RefCell::new(Vec2 { x: 50.0, y: 5.0 });
|
|
||||||
let ud = lua.create_userdata(UserdataRef::from(v1.borrow()))?;
|
|
||||||
globals.set("v1", ud)?;
|
|
||||||
|
|
||||||
let chunk = lua.load(
|
|
||||||
"text.lua",
|
|
||||||
r#"
|
|
||||||
print("v1: (" .. v1.x .. ", " .. v1.y .. ")")
|
|
||||||
"#)?;
|
|
||||||
|
|
||||||
for _ in 0..40 {
|
|
||||||
let res = lua.execute_chunk::<_, Value>(&chunk, ());
|
|
||||||
|
|
||||||
if let Err(e) = res {
|
|
||||||
panic!("{}", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
//println!("i = {}", i);
|
|
||||||
globals.set("v1", Value::Nil)?;
|
|
||||||
lua.gc_collect(); // must collect here to drop the Ref
|
|
||||||
|
|
||||||
let mut t = v1.borrow_mut();
|
|
||||||
t.x += 50.0;
|
|
||||||
t.y += 5.0;
|
|
||||||
drop(t);
|
|
||||||
|
|
||||||
let ud = lua.create_userdata(UserdataRef::from(v1.borrow()))?;
|
|
||||||
globals.raw_set("v1", ud)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This test ensures that a raw borrow of userdata can be provided to Lua, and it that it can run non-mutating methods on the userdata.
|
|
||||||
#[test]
|
|
||||||
fn ud_methods_borrow() -> crate::Result<()> {
|
|
||||||
let lua = State::new();
|
|
||||||
lua.expose_libraries(&[StdLibrary::Debug, StdLibrary::Package]);
|
|
||||||
|
|
||||||
let globals = lua.globals()?;
|
|
||||||
|
|
||||||
let v1 = RefCell::new(Vec2 { x: 50.0, y: 5.0 });
|
|
||||||
let ud = lua.create_userdata(UserdataRef::from(v1.borrow()))?;
|
|
||||||
globals.set("v1", ud)?;
|
|
||||||
|
|
||||||
let chunk = lua.load(
|
|
||||||
"text.lua",
|
|
||||||
r#"
|
|
||||||
v2 = v1:mult_ret(2.0)
|
|
||||||
print("v2: (" .. v2.x .. ", " .. v2.y .. ")")
|
|
||||||
"#)?;
|
|
||||||
|
|
||||||
let res = lua.execute_chunk::<_, Value>(&chunk, ());
|
|
||||||
|
|
||||||
if let Err(e) = res {
|
|
||||||
panic!("{}", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
let v2 = globals.get::<_, Ref<Vec2>>("v2")?;
|
|
||||||
assert_eq!(v2.x, 100.0);
|
|
||||||
assert_eq!(v2.y, 10.0);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,195 +0,0 @@
|
||||||
use std::{cell::RefMut, mem, ops::{Deref, DerefMut}};
|
|
||||||
|
|
||||||
use crate::{AnyUserdata, Result, State, Userdata, UserdataBuilder};
|
|
||||||
|
|
||||||
enum BorrowMut<'a, T> {
|
|
||||||
Wrapped(RefMut<'a, T>),
|
|
||||||
Raw(&'a mut T),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T> Deref for BorrowMut<'a, T> {
|
|
||||||
type Target = T;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
match self {
|
|
||||||
BorrowMut::Wrapped(w) => w,
|
|
||||||
BorrowMut::Raw(w) => w,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T> DerefMut for BorrowMut<'a, T> {
|
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
||||||
match self {
|
|
||||||
BorrowMut::Wrapped(w) => w,
|
|
||||||
BorrowMut::Raw(w) => w,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct UserdataRefMut<'a, T: Userdata> {
|
|
||||||
borrow: BorrowMut<'a, T>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T: Userdata> From<&'a mut T> for UserdataRefMut<'static, T> {
|
|
||||||
fn from(value: &'a mut T) -> Self {
|
|
||||||
let ud = BorrowMut::Raw(value);
|
|
||||||
|
|
||||||
Self {
|
|
||||||
borrow: unsafe {
|
|
||||||
mem::transmute(ud)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T: Userdata> From<RefMut<'a, T>> for UserdataRefMut<'static, T> {
|
|
||||||
fn from(value: RefMut<'a, T>) -> Self {
|
|
||||||
let ud = BorrowMut::Wrapped(value);
|
|
||||||
|
|
||||||
Self {
|
|
||||||
borrow: unsafe {
|
|
||||||
mem::transmute(ud)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T: Userdata + 'static> Userdata for UserdataRefMut<'a, T> {
|
|
||||||
fn build<'b>(state: &State, builder: &mut UserdataBuilder<'b, Self>) -> crate::Result<()> {
|
|
||||||
let mut other = UserdataBuilder::<T>::new();
|
|
||||||
T::build(state, &mut other)?;
|
|
||||||
|
|
||||||
builder.expand_with(other);
|
|
||||||
|
|
||||||
let getter: fn(AnyUserdata<'_>) -> Result<*const ()> = move |ud: AnyUserdata| {
|
|
||||||
let ud_ptr = {
|
|
||||||
let ud = ud.as_ref::<UserdataRefMut<T>>()?;
|
|
||||||
|
|
||||||
let ud_ptr: *const T = &*ud.borrow;
|
|
||||||
ud_ptr
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(ud_ptr.cast::<()>())
|
|
||||||
};
|
|
||||||
|
|
||||||
let getter_mut: fn(AnyUserdata<'_>) -> Result<*mut ()> = move |ud: AnyUserdata| {
|
|
||||||
let ud_ptr = {
|
|
||||||
let mut ud = ud.as_mut::<UserdataRefMut<T>>()?;
|
|
||||||
|
|
||||||
let ud_ptr: *mut T = &mut *ud.borrow;
|
|
||||||
ud_ptr
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(ud_ptr.cast::<()>())
|
|
||||||
};
|
|
||||||
|
|
||||||
if builder.wrapped_getter.set(Box::new(getter)).is_err() {
|
|
||||||
panic!("Somehow the wrapped getter has already been set");
|
|
||||||
}
|
|
||||||
|
|
||||||
if builder.wrapped_getter_mut.set(Box::new(getter_mut)).is_err() {
|
|
||||||
panic!("Somehow the wrapped mutable getter has already been set");
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn name() -> String {
|
|
||||||
let name = format!("{}RefMut", T::name());
|
|
||||||
name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use std::cell::RefCell;
|
|
||||||
|
|
||||||
use crate::{tests::Vec2, State, StdLibrary, Value};
|
|
||||||
|
|
||||||
use super::UserdataRefMut;
|
|
||||||
|
|
||||||
/// This test ensures that you can provide Lua a RefMut and it that it can set the fields on the userdata.
|
|
||||||
#[test]
|
|
||||||
fn ud_fields_borrow_mut() -> crate::Result<()> {
|
|
||||||
let lua = State::new();
|
|
||||||
lua.expose_libraries(&[StdLibrary::Debug, StdLibrary::Package]);
|
|
||||||
|
|
||||||
let globals = lua.globals()?;
|
|
||||||
|
|
||||||
let v1 = RefCell::new(Vec2 { x: 50.0, y: 5.0 });
|
|
||||||
let ud = lua.create_userdata(UserdataRefMut::from(v1.borrow_mut()))?;
|
|
||||||
globals.set("v1", ud)?;
|
|
||||||
|
|
||||||
let mut x = 50.0;
|
|
||||||
let mut y = 5.0;
|
|
||||||
|
|
||||||
let chunk = lua.load(
|
|
||||||
"text.lua",
|
|
||||||
r#"
|
|
||||||
v1.x = v1.x + 50
|
|
||||||
v1.y = v1.y + 5
|
|
||||||
print("v1: (" .. v1.x .. ", " .. v1.y .. ")")
|
|
||||||
"#)?;
|
|
||||||
|
|
||||||
for _ in 0..40 {
|
|
||||||
let res = lua.execute_chunk::<_, Value>(&chunk, ());
|
|
||||||
|
|
||||||
if let Err(e) = res {
|
|
||||||
panic!("{}", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
//println!("i = {}", i);
|
|
||||||
globals.set("v1", Value::Nil)?;
|
|
||||||
lua.gc_collect(); // must collect here to drop the RefMut
|
|
||||||
|
|
||||||
x += 50.0;
|
|
||||||
y += 5.0;
|
|
||||||
|
|
||||||
let t = v1.borrow();
|
|
||||||
assert_eq!(x, t.x);
|
|
||||||
assert_eq!(y, t.y);
|
|
||||||
drop(t);
|
|
||||||
|
|
||||||
let ud = lua.create_userdata(UserdataRefMut::from(v1.borrow_mut()))?;
|
|
||||||
globals.raw_set("v1", ud)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This test ensures that a RefMut of userdata can be provided to Lua, and it that it can run mutating methods on the userdata.
|
|
||||||
#[test]
|
|
||||||
fn ud_methods_borrow_mut() -> crate::Result<()> {
|
|
||||||
let lua = State::new();
|
|
||||||
lua.expose_libraries(&[StdLibrary::Debug, StdLibrary::Package]);
|
|
||||||
|
|
||||||
let globals = lua.globals()?;
|
|
||||||
|
|
||||||
let v1 = RefCell::new(Vec2 { x: 50.0, y: 5.0 });
|
|
||||||
let ud = lua.create_userdata(UserdataRefMut::from(v1.borrow_mut()))?;
|
|
||||||
globals.set("v1", ud)?;
|
|
||||||
|
|
||||||
let chunk = lua.load(
|
|
||||||
"text.lua",
|
|
||||||
r#"
|
|
||||||
v1:mult(2.0)
|
|
||||||
print("v1: (" .. v1.x .. ", " .. v1.y .. ")")
|
|
||||||
"#)?;
|
|
||||||
|
|
||||||
let res = lua.execute_chunk::<_, Value>(&chunk, ());
|
|
||||||
|
|
||||||
if let Err(e) = res {
|
|
||||||
panic!("{}", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
globals.set("v1", Value::Nil)?;
|
|
||||||
lua.gc_collect(); // must collect here to drop the RefMut
|
|
||||||
|
|
||||||
let t = v1.borrow();
|
|
||||||
assert_eq!(100.0, t.x);
|
|
||||||
assert_eq!(10.0, t.y);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,34 +0,0 @@
|
||||||
use std::marker::PhantomData;
|
|
||||||
|
|
||||||
use crate::{AsLua, State, Userdata, UserdataBuilder, Value};
|
|
||||||
|
|
||||||
pub struct UserdataProxy<T: Userdata>(PhantomData<T>);
|
|
||||||
|
|
||||||
impl<T: Userdata> UserdataProxy<T> {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self(PhantomData)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T: Userdata> AsLua<'a> for UserdataProxy<T> {
|
|
||||||
fn as_lua(&self, _lua: &'a State) -> crate::Result<Value<'a>> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Userdata> Userdata for UserdataProxy<T> {
|
|
||||||
fn build<'a>(state: &State, builder: &mut UserdataBuilder<'a, Self>) -> crate::Result<()> {
|
|
||||||
let mut other = UserdataBuilder::<T>::new();
|
|
||||||
T::build(state, &mut other)?;
|
|
||||||
|
|
||||||
// only the functions need to be added since they're the only thing usable from a proxy
|
|
||||||
builder.functions = other.functions;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn name() -> String {
|
|
||||||
let name = format!("{}Proxy", T::name());
|
|
||||||
name
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,180 +0,0 @@
|
||||||
use std::ffi::CStr;
|
|
||||||
|
|
||||||
use crate::{ensure_type, FromLuaStack, LuaRef, PushToLuaStack, StackGuard, State, Userdata};
|
|
||||||
|
|
||||||
use mlua_sys as lua;
|
|
||||||
|
|
||||||
pub struct UnsafeUserdata<'a> {
|
|
||||||
pub(crate) lref: LuaRef<'a>,
|
|
||||||
state: &'a State,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Clone for UnsafeUserdata<'a> {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
Self {
|
|
||||||
lref: self.lref.clone(),
|
|
||||||
state: self.state,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> UnsafeUserdata<'a> {
|
|
||||||
pub fn from_ref(state: &'a State, lref: LuaRef<'a>) -> Self {
|
|
||||||
Self {
|
|
||||||
lref,
|
|
||||||
state,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a borrow to the userdata.
|
|
||||||
pub fn as_ref<T: Userdata + 'static>(&self) -> crate::Result<&'a T> {
|
|
||||||
unsafe {
|
|
||||||
self.state.ensure_stack(3)?;
|
|
||||||
let _g = StackGuard::new(self.state);
|
|
||||||
let s = self.state.state_ptr();
|
|
||||||
|
|
||||||
self.lref.push_to_lua_stack(self.state)?;
|
|
||||||
|
|
||||||
if lua::lua_getmetatable(s, -1) == 0 {
|
|
||||||
return Err(crate::Error::UserdataMismatch);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.state.get_userdata_metatable::<T>()
|
|
||||||
.push_to_lua_stack(self.state)?;
|
|
||||||
|
|
||||||
if lua::lua_rawequal(s, -2, -1) == 1 {
|
|
||||||
let cptr = lua::lua_touserdata(s, -3);
|
|
||||||
let t = &*cptr.cast::<T>();
|
|
||||||
Ok(t)
|
|
||||||
} else {
|
|
||||||
return Err(crate::Error::UserdataMismatch);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a mutable reference to the userdata.
|
|
||||||
pub fn as_mut<T: Userdata + 'static>(&self) -> crate::Result<&'a mut T> {
|
|
||||||
unsafe {
|
|
||||||
self.state.ensure_stack(3)?;
|
|
||||||
let _g = StackGuard::new(self.state);
|
|
||||||
let s = self.state.state_ptr();
|
|
||||||
|
|
||||||
self.lref.push_to_lua_stack(self.state)?;
|
|
||||||
|
|
||||||
if lua::lua_getmetatable(s, -1) == 0 {
|
|
||||||
return Err(crate::Error::MissingMetatable);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.state.get_userdata_metatable::<T>()
|
|
||||||
.push_to_lua_stack(self.state)?;
|
|
||||||
|
|
||||||
if lua::lua_rawequal(s, -2, -1) == 1 {
|
|
||||||
let cptr = lua::lua_touserdata(s, -3);
|
|
||||||
let t = &mut *cptr.cast::<T>();
|
|
||||||
Ok(t)
|
|
||||||
} else {
|
|
||||||
Err(crate::Error::UserdataMismatch)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a mutable pointer of the [`RefCell`] of userdata **WITHOUT verifying the type of it**.
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
/// * You must be certain that the type `T` is the same type that this userdata has a handle to.
|
|
||||||
/// There is a blind cast from `void*` to `T*`
|
|
||||||
///
|
|
||||||
/// If there is a possibility that these types do not match, use [`UnsafeUserdata::as_ptr`]
|
|
||||||
/// which does verify the types.
|
|
||||||
pub unsafe fn as_ptr_unchecked<T>(&self) -> crate::Result<*mut T> {
|
|
||||||
self.state.ensure_stack(1)?;
|
|
||||||
let _g = StackGuard::new(self.state);
|
|
||||||
let s = self.state.state_ptr();
|
|
||||||
|
|
||||||
self.lref.push_to_lua_stack(self.state)?;
|
|
||||||
let cptr = lua::lua_touserdata(s, -1);
|
|
||||||
|
|
||||||
Ok(cptr.cast())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a mutable pointer of the data stored.
|
|
||||||
///
|
|
||||||
/// This function ensures that the type of the userdata this struct has a handle to is the
|
|
||||||
/// same as `T`. If it isn't, a `UserdataMismatch` error will be returned.
|
|
||||||
pub unsafe fn as_ptr<T: Userdata + 'static>(&self) -> crate::Result<*mut T> {
|
|
||||||
let _g = StackGuard::new(self.state);
|
|
||||||
let s = self.state.state_ptr();
|
|
||||||
|
|
||||||
self.lref.push_to_lua_stack(self.state)?;
|
|
||||||
|
|
||||||
if lua::lua_getmetatable(s, -1) == 0 {
|
|
||||||
return Err(crate::Error::MissingMetatable);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.state.get_userdata_metatable::<T>()
|
|
||||||
.push_to_lua_stack(self.state)?;
|
|
||||||
|
|
||||||
if lua::lua_rawequal(s, -2, -1) == 1 {
|
|
||||||
let cptr = lua::lua_touserdata(s, -3);
|
|
||||||
Ok(cptr.cast())
|
|
||||||
} else {
|
|
||||||
Err(crate::Error::UserdataMismatch)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the name of the userdata by accessing the metatable
|
|
||||||
pub fn name(&self) -> crate::Result<String> {
|
|
||||||
unsafe {
|
|
||||||
self.state.ensure_stack(3)?;
|
|
||||||
let _g = StackGuard::new(self.state);
|
|
||||||
|
|
||||||
self.lref.push_to_lua_stack(self.state)?;
|
|
||||||
let s = self.state.state_ptr();
|
|
||||||
|
|
||||||
if lua::lua_getmetatable(s, -1) == 0 {
|
|
||||||
return Err(crate::Error::MissingMetatable);
|
|
||||||
}
|
|
||||||
|
|
||||||
lua::lua_pushliteral(s, "__name");
|
|
||||||
lua::lua_rawget(s, -2);
|
|
||||||
|
|
||||||
ensure_type(self.state, lua::LUA_TSTRING, -1)?;
|
|
||||||
|
|
||||||
let cstr = CStr::from_ptr(lua::lua_tostring(s, -1));
|
|
||||||
let cstr = cstr.to_str()
|
|
||||||
// on panic, this should be considered a bug
|
|
||||||
.expect("Metatable name has invalid utf8 bytes!");
|
|
||||||
|
|
||||||
Ok(cstr.to_string())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> FromLuaStack<'a> for UnsafeUserdata<'a> {
|
|
||||||
unsafe fn from_lua_stack(state: &'a State) -> crate::Result<Self> {
|
|
||||||
ensure_type(state, lua::LUA_TUSERDATA, -1)?;
|
|
||||||
|
|
||||||
Ok(UnsafeUserdata {
|
|
||||||
lref: LuaRef::from_stack(state)?,
|
|
||||||
state,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> PushToLuaStack<'a> for UnsafeUserdata<'a> {
|
|
||||||
unsafe fn push_to_lua_stack(&self, state: &'a State) -> crate::Result<()> {
|
|
||||||
self.lref.push_to_lua_stack(state)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* impl<'a> AsLua<'a> for UnsafeUserdata<'a> {
|
|
||||||
fn as_lua(&self, _lua: &'a State) -> crate::Result<Value<'a>> {
|
|
||||||
Ok(Value::Userdata(self.clone()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> FromLua<'a> for UnsafeUserdata<'a> {
|
|
||||||
fn from_lua(_lua: &State, val: Value<'a>) -> crate::Result<Self> {
|
|
||||||
val.into_userdata()
|
|
||||||
}
|
|
||||||
} */
|
|
40
src/util.rs
40
src/util.rs
|
@ -1,10 +1,10 @@
|
||||||
use std::ffi::CStr;
|
use std::ffi::CStr;
|
||||||
|
|
||||||
use crate::{FromLuaStack, PushToLuaStack, StackGuard, State, Value};
|
use crate::State;
|
||||||
|
|
||||||
use mlua_sys as lua;
|
use mlua_sys as lua;
|
||||||
|
|
||||||
pub(crate) unsafe fn ensure_type(state: &State, typ: i32, idx: i32) -> crate::Result<()> {
|
pub unsafe fn ensure_type(state: &State, typ: i32, idx: i32) -> crate::Result<()> {
|
||||||
let s = state.state_ptr();
|
let s = state.state_ptr();
|
||||||
|
|
||||||
let lua_type = lua::lua_type(s, idx);
|
let lua_type = lua::lua_type(s, idx);
|
||||||
|
@ -24,40 +24,4 @@ pub(crate) unsafe fn ensure_type(state: &State, typ: i32, idx: i32) -> crate::Re
|
||||||
|
|
||||||
Err(crate::Error::UnexpectedType(exp_s.to_string(), s.to_string()))
|
Err(crate::Error::UnexpectedType(exp_s.to_string(), s.to_string()))
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/// Iterates through all entries in the registry and prints the types of each entry.
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub(crate) fn print_registry(lua: &State) -> crate::Result<()> {
|
|
||||||
unsafe {
|
|
||||||
let _g = StackGuard::new(&lua);
|
|
||||||
let s = lua.state_ptr();
|
|
||||||
|
|
||||||
Value::String(String::from("Hello World")).push_to_lua_stack(&lua)?;
|
|
||||||
let r = lua::luaL_ref(s, lua::LUA_REGISTRYINDEX);
|
|
||||||
lua::luaL_unref(s, lua::LUA_REGISTRYINDEX, r);
|
|
||||||
|
|
||||||
for i in 0..r {
|
|
||||||
let ty = lua::lua_rawgeti(s, lua::LUA_REGISTRYINDEX, i as i64);
|
|
||||||
|
|
||||||
match ty {
|
|
||||||
lua::LUA_TUSERDATA => {
|
|
||||||
lua::lua_getmetatable(s, -1);
|
|
||||||
lua::lua_pushliteral(s, "__name");
|
|
||||||
lua::lua_rawget(s, -2);
|
|
||||||
|
|
||||||
let v = Value::from_lua_stack(&lua)?;
|
|
||||||
println!("{}: {}", i, v.as_string().unwrap());
|
|
||||||
},
|
|
||||||
_ => {
|
|
||||||
let tyname = CStr::from_ptr(lua::lua_typename(s, ty));
|
|
||||||
let tyname = tyname.to_str().unwrap();
|
|
||||||
println!("{}: {}", i, tyname);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
lua::lua_pop(s, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
106
src/value.rs
106
src/value.rs
|
@ -36,17 +36,27 @@ impl<'a> Value<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_number(&self) -> crate::Result<f64> {
|
pub fn as_userdata(&self) -> crate::Result<&AnyUserdata> {
|
||||||
match self {
|
match self {
|
||||||
Value::Number(v) => Ok(*v),
|
Value::Userdata(ud) => Ok(ud),
|
||||||
_ => {
|
_ => {
|
||||||
Err(crate::Error::UnexpectedType("Number".to_string(), self.type_name().to_string()))
|
Err(crate::Error::UnexpectedType("Userdata".to_string(), self.type_name().to_string()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Consumes self, and attempts to get `AnyUserdata`.
|
||||||
|
///
|
||||||
|
/// Returns an error if this value is not userdata
|
||||||
|
pub fn into_userdata(self) -> crate::Result<AnyUserdata<'a>> {
|
||||||
|
match self {
|
||||||
|
Value::Userdata(ud) => Ok(ud),
|
||||||
|
_ => {
|
||||||
|
Err(crate::Error::UnexpectedType("Userdata".to_string(), self.type_name().to_string()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// If `self` is an instance of Value::String, returns a borrow to it. If it is not then
|
|
||||||
/// an `UnexpectedType` error is returned.
|
|
||||||
pub fn as_string(&self) -> crate::Result<&String> {
|
pub fn as_string(&self) -> crate::Result<&String> {
|
||||||
match self {
|
match self {
|
||||||
Value::String(s) => Ok(s),
|
Value::String(s) => Ok(s),
|
||||||
|
@ -56,41 +66,6 @@ impl<'a> Value<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// If `self` is an instance of Value::Function, returns a borrow to it. If it is not then
|
|
||||||
/// an `UnexpectedType` error is returned.
|
|
||||||
pub fn as_function(&self) -> crate::Result<Function> {
|
|
||||||
match self {
|
|
||||||
Value::Function(v) => Ok(v.clone()),
|
|
||||||
_ => {
|
|
||||||
Err(crate::Error::UnexpectedType("Table".to_string(), self.type_name().to_string()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// If `self` is an instance of Value::Table, returns a borrow to it. If it is not then
|
|
||||||
/// an `UnexpectedType` error is returned.
|
|
||||||
pub fn as_table(&self) -> crate::Result<Table> {
|
|
||||||
match self {
|
|
||||||
Value::Table(v) => Ok(v.clone()),
|
|
||||||
_ => {
|
|
||||||
Err(crate::Error::UnexpectedType("Table".to_string(), self.type_name().to_string()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// If `self` is an instance of Value::Userdata, returns a borrow to it. If it is not then
|
|
||||||
/// an `UnexpectedType` error is returned.
|
|
||||||
pub fn as_userdata(&self) -> crate::Result<AnyUserdata<'a>> {
|
|
||||||
match self {
|
|
||||||
Value::Userdata(ud) => Ok(ud.clone()),
|
|
||||||
_ => {
|
|
||||||
Err(crate::Error::UnexpectedType("Userdata".to_string(), self.type_name().to_string()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// If `self` is an instance of Value::String, the string is returned. If it is not then
|
|
||||||
/// an `UnexpectedType` error is returned.
|
|
||||||
pub fn into_string(self) -> crate::Result<String> {
|
pub fn into_string(self) -> crate::Result<String> {
|
||||||
match self {
|
match self {
|
||||||
Value::String(s) => Ok(s),
|
Value::String(s) => Ok(s),
|
||||||
|
@ -99,43 +74,10 @@ impl<'a> Value<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// If `self` is an instance of Value::Function, the function is returned. If it is not then
|
|
||||||
/// an `UnexpectedType` error is returned.
|
|
||||||
pub fn into_function(self) -> crate::Result<Function<'a>> {
|
|
||||||
match self {
|
|
||||||
Value::Function(v) => Ok(v),
|
|
||||||
_ => {
|
|
||||||
Err(crate::Error::UnexpectedType("Function".to_string(), self.type_name().to_string()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// If `self` is an instance of Value::Table, the table is returned. If it is not then
|
|
||||||
/// an `UnexpectedType` error is returned.
|
|
||||||
pub fn into_table(self) -> crate::Result<Table<'a>> {
|
|
||||||
match self {
|
|
||||||
Value::Table(v) => Ok(v),
|
|
||||||
_ => {
|
|
||||||
Err(crate::Error::UnexpectedType("Table".to_string(), self.type_name().to_string()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// If `self` is an instance of Value::Userdata, the userdata is returned. If it is not then
|
|
||||||
/// an `UnexpectedType` error is returned.
|
|
||||||
pub fn into_userdata(self) -> crate::Result<AnyUserdata<'a>> {
|
|
||||||
match self {
|
|
||||||
Value::Userdata(ud) => Ok(ud),
|
|
||||||
_ => {
|
|
||||||
Err(crate::Error::UnexpectedType("Userdata".to_string(), self.type_name().to_string()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> PushToLuaStack<'a> for Value<'a> {
|
impl<'a> PushToLuaStack<'a> for Value<'a> {
|
||||||
unsafe fn push_to_lua_stack(&self, state: &'a crate::State) -> crate::Result<()> {
|
unsafe fn push_to_lua_stack(&self, state: &crate::State) -> crate::Result<()> {
|
||||||
let s = state.state_ptr();
|
let s = state.state_ptr();
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
|
@ -362,16 +304,6 @@ impl<'a> FromLuaVec<'a> for ValueVec<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> PushToLuaStack<'a> for ValueVec<'a> {
|
|
||||||
unsafe fn push_to_lua_stack(&self, state: &'a State) -> crate::Result<()> {
|
|
||||||
for v in self.iter() {
|
|
||||||
v.push_to_lua_stack(state)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T: FromLua<'a>> FromLuaVec<'a> for T {
|
impl<'a, T: FromLua<'a>> FromLuaVec<'a> for T {
|
||||||
fn from_lua_value_vec(state: &'a State, mut values: ValueVec<'a>) -> crate::Result<Self> {
|
fn from_lua_value_vec(state: &'a State, mut values: ValueVec<'a>) -> crate::Result<Self> {
|
||||||
if let Some(val) = values.pop_front() {
|
if let Some(val) = values.pop_front() {
|
||||||
|
@ -385,12 +317,6 @@ impl<'a, T: FromLua<'a>> FromLuaVec<'a> for T {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> IntoLuaVec<'a> for () {
|
|
||||||
fn into_lua_value_vec(self, _state: &'a State) -> crate::Result<ValueVec<'a>> {
|
|
||||||
Ok(ValueVec::new())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! impl_from_lua_vec_tuple {
|
macro_rules! impl_from_lua_vec_tuple {
|
||||||
( $count: expr, $first: tt, $( $name: tt ),+ ) => (
|
( $count: expr, $first: tt, $( $name: tt ),+ ) => (
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
|
|
Loading…
Reference in New Issue