Add a dedicated type for Luau vector.
Refactor existing implementation and add 4-dimensional vectors support.
This commit is contained in:
parent
c2bfc9ec52
commit
1f0e81c9a1
|
@ -9,7 +9,7 @@ jobs:
|
|||
matrix:
|
||||
os: [ubuntu-22.04, macos-latest, windows-latest]
|
||||
rust: [stable]
|
||||
lua: [lua54, lua53, lua52, lua51, luajit, luau, luau-jit]
|
||||
lua: [lua54, lua53, lua52, lua51, luajit, luau, luau-jit, luau-vector4]
|
||||
include:
|
||||
- os: ubuntu-22.04
|
||||
target: x86_64-unknown-linux-gnu
|
||||
|
@ -104,7 +104,7 @@ jobs:
|
|||
matrix:
|
||||
os: [ubuntu-22.04, macos-latest, windows-latest]
|
||||
rust: [stable, nightly]
|
||||
lua: [lua54, lua53, lua52, lua51, luajit, luajit52, luau, luau-jit]
|
||||
lua: [lua54, lua53, lua52, lua51, luajit, luajit52, luau, luau-jit, luau-vector4]
|
||||
include:
|
||||
- os: ubuntu-22.04
|
||||
target: x86_64-unknown-linux-gnu
|
||||
|
@ -140,7 +140,7 @@ jobs:
|
|||
matrix:
|
||||
os: [ubuntu-22.04]
|
||||
rust: [nightly]
|
||||
lua: [lua54, lua53, lua52, lua51, luajit, luau, luau-jit]
|
||||
lua: [lua54, lua53, lua52, lua51, luajit, luau, luau-jit, luau-vector4]
|
||||
include:
|
||||
- os: ubuntu-22.04
|
||||
target: x86_64-unknown-linux-gnu
|
||||
|
@ -222,7 +222,7 @@ jobs:
|
|||
runs-on: ubuntu-22.04
|
||||
strategy:
|
||||
matrix:
|
||||
lua: [lua54, lua53, lua52, lua51, luajit, luau, luau-jit]
|
||||
lua: [lua54, lua53, lua52, lua51, luajit, luau, luau-jit, luau-vector4]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
|
|
|
@ -33,6 +33,7 @@ luajit = ["ffi/luajit"]
|
|||
luajit52 = ["luajit", "ffi/luajit52"]
|
||||
luau = ["ffi/luau"]
|
||||
luau-jit = ["luau", "ffi/luau-codegen"]
|
||||
luau-vector4 = ["luau", "ffi/luau-vector4"]
|
||||
vendored = ["ffi/vendored"]
|
||||
module = ["mlua_derive", "ffi/module"]
|
||||
async = ["futures-util"]
|
||||
|
|
|
@ -46,6 +46,7 @@ Below is a list of the available feature flags. By default `mlua` does not enabl
|
|||
* `luajit52`: activate [LuaJIT] support with partial compatibility with Lua 5.2
|
||||
* `luau`: activate [Luau] support (auto vendored mode)
|
||||
* `luau-jit`: activate [Luau] support with experimental jit backend. This is unstable feature and not recommended to use.
|
||||
* `luau-vector4`: activate [Luau] support with 4-dimensional vector.
|
||||
* `vendored`: build static Lua(JIT) library from sources during `mlua` compilation using [lua-src] or [luajit-src] crates
|
||||
* `module`: enable module mode (building loadable `cdylib` library for Lua)
|
||||
* `async`: enable async/await support (any executor can be used, eg. [tokio] or [async-std])
|
||||
|
|
|
@ -27,6 +27,7 @@ luajit = []
|
|||
luajit52 = ["luajit"]
|
||||
luau = ["luau0-src"]
|
||||
luau-codegen = ["luau"]
|
||||
luau-vector4 = ["luau"]
|
||||
vendored = ["lua-src", "luajit-src"]
|
||||
module = []
|
||||
|
||||
|
@ -38,4 +39,4 @@ cfg-if = "1.0"
|
|||
pkg-config = "0.3.17"
|
||||
lua-src = { version = ">= 546.0.0, < 550.0.0", optional = true }
|
||||
luajit-src = { version = ">= 210.4.0, < 220.0.0", optional = true }
|
||||
luau0-src = { version = "0.5.8", optional = true }
|
||||
luau0-src = { version = "0.5.10", optional = true }
|
||||
|
|
|
@ -23,6 +23,7 @@ pub fn probe_lua() -> Option<PathBuf> {
|
|||
#[cfg(feature = "luau")]
|
||||
let artifacts = luau0_src::Build::new()
|
||||
.enable_codegen(cfg!(feature = "luau-codegen"))
|
||||
.set_vector_size(if cfg!(feature = "luau-vector4") { 4 } else { 3 })
|
||||
.build();
|
||||
|
||||
artifacts.print_cargo_metadata();
|
||||
|
|
|
@ -156,7 +156,10 @@ extern "C" {
|
|||
pub fn lua_pushnumber(L: *mut lua_State, n: lua_Number);
|
||||
pub fn lua_pushinteger(L: *mut lua_State, n: lua_Integer);
|
||||
pub fn lua_pushunsigned(L: *mut lua_State, n: lua_Unsigned);
|
||||
#[cfg(not(feature = "luau-vector4"))]
|
||||
pub fn lua_pushvector(L: *mut lua_State, x: c_float, y: c_float, z: c_float);
|
||||
#[cfg(feature = "luau-vector4")]
|
||||
pub fn lua_pushvector(L: *mut lua_State, x: c_float, y: c_float, z: c_float, w: c_float);
|
||||
#[link_name = "lua_pushlstring"]
|
||||
pub fn lua_pushlstring_(L: *mut lua_State, s: *const c_char, l: usize);
|
||||
#[link_name = "lua_pushstring"]
|
||||
|
|
|
@ -292,6 +292,29 @@ impl<'lua> FromLua<'lua> for LightUserData {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
impl<'lua> IntoLua<'lua> for crate::types::Vector {
|
||||
#[inline]
|
||||
fn into_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
||||
Ok(Value::Vector(self))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
impl<'lua> FromLua<'lua> for crate::types::Vector {
|
||||
#[inline]
|
||||
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Self> {
|
||||
match value {
|
||||
Value::Vector(v) => Ok(v),
|
||||
_ => Err(Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: "vector",
|
||||
message: None,
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> IntoLua<'lua> for StdString {
|
||||
#[inline]
|
||||
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
|
@ -561,16 +584,17 @@ where
|
|||
fn from_lua(value: Value<'lua>, _lua: &'lua Lua) -> Result<Self> {
|
||||
match value {
|
||||
#[cfg(feature = "luau")]
|
||||
Value::Vector(x, y, z) if N == 3 => Ok(mlua_expect!(
|
||||
vec![
|
||||
T::from_lua(Value::Number(x as _), _lua)?,
|
||||
T::from_lua(Value::Number(y as _), _lua)?,
|
||||
T::from_lua(Value::Number(z as _), _lua)?,
|
||||
]
|
||||
.try_into()
|
||||
.map_err(|_| ()),
|
||||
"cannot convert vector to array"
|
||||
)),
|
||||
#[rustfmt::skip]
|
||||
Value::Vector(v) if N == crate::types::Vector::SIZE => unsafe {
|
||||
use std::{mem, ptr};
|
||||
let mut arr: [mem::MaybeUninit<T>; N] = mem::MaybeUninit::uninit().assume_init();
|
||||
ptr::write(arr[0].as_mut_ptr() , T::from_lua(Value::Number(v.x() as _), _lua)?);
|
||||
ptr::write(arr[1].as_mut_ptr(), T::from_lua(Value::Number(v.y() as _), _lua)?);
|
||||
ptr::write(arr[2].as_mut_ptr(), T::from_lua(Value::Number(v.z() as _), _lua)?);
|
||||
#[cfg(feature = "luau-vector4")]
|
||||
ptr::write(arr[3].as_mut_ptr(), T::from_lua(Value::Number(v.w() as _), _lua)?);
|
||||
Ok(mem::transmute_copy(&arr))
|
||||
},
|
||||
Value::Table(table) => {
|
||||
let vec = table.sequence_values().collect::<Result<Vec<_>>>()?;
|
||||
vec.try_into()
|
||||
|
@ -614,12 +638,6 @@ impl<'lua, T: FromLua<'lua>> FromLua<'lua> for Vec<T> {
|
|||
#[inline]
|
||||
fn from_lua(value: Value<'lua>, _lua: &'lua Lua) -> Result<Self> {
|
||||
match value {
|
||||
#[cfg(feature = "luau")]
|
||||
Value::Vector(x, y, z) => Ok(vec![
|
||||
T::from_lua(Value::Number(x as _), _lua)?,
|
||||
T::from_lua(Value::Number(y as _), _lua)?,
|
||||
T::from_lua(Value::Number(z as _), _lua)?,
|
||||
]),
|
||||
Value::Table(table) => table.sequence_values().collect(),
|
||||
_ => Err(Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
|
|
|
@ -130,7 +130,11 @@ pub use crate::hook::HookTriggers;
|
|||
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
pub use crate::{chunk::Compiler, function::CoverageInfo, types::VmState};
|
||||
pub use crate::{
|
||||
chunk::Compiler,
|
||||
function::CoverageInfo,
|
||||
types::{Vector, VmState},
|
||||
};
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
pub use crate::thread::AsyncThread;
|
||||
|
|
17
src/lua.rs
17
src/lua.rs
|
@ -50,7 +50,10 @@ use crate::{hook::HookTriggers, types::HookCallback};
|
|||
#[cfg(feature = "luau")]
|
||||
use crate::types::InterruptCallback;
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
use crate::{chunk::Compiler, types::VmState};
|
||||
use crate::{
|
||||
chunk::Compiler,
|
||||
types::{Vector, VmState},
|
||||
};
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use {
|
||||
|
@ -2295,8 +2298,11 @@ impl Lua {
|
|||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
Value::Vector(x, y, z) => {
|
||||
ffi::lua_pushvector(state, x, y, z);
|
||||
Value::Vector(v) => {
|
||||
#[cfg(not(feature = "luau-vector4"))]
|
||||
ffi::lua_pushvector(state, v.x(), v.y(), v.z());
|
||||
#[cfg(feature = "luau-vector4")]
|
||||
ffi::lua_pushvector(state, v.x(), v.y(), v.z(), v.w());
|
||||
}
|
||||
|
||||
Value::String(s) => {
|
||||
|
@ -2379,7 +2385,10 @@ impl Lua {
|
|||
ffi::LUA_TVECTOR => {
|
||||
let v = ffi::lua_tovector(state, -1);
|
||||
mlua_debug_assert!(!v.is_null(), "vector is null");
|
||||
let vec = Value::Vector(*v, *v.add(1), *v.add(2));
|
||||
#[cfg(not(feature = "luau-vector4"))]
|
||||
let vec = Value::Vector(Vector([*v, *v.add(1), *v.add(2)]));
|
||||
#[cfg(feature = "luau-vector4")]
|
||||
let vec = Value::Vector(Vector([*v, *v.add(1), *v.add(2), *v.add(3)]));
|
||||
ffi::lua_pop(state, 1);
|
||||
vec
|
||||
}
|
||||
|
|
|
@ -126,6 +126,12 @@ unsafe extern "C" fn lua_vector(state: *mut ffi::lua_State) -> c_int {
|
|||
let x = ffi::luaL_checknumber(state, 1) as c_float;
|
||||
let y = ffi::luaL_checknumber(state, 2) as c_float;
|
||||
let z = ffi::luaL_checknumber(state, 3) as c_float;
|
||||
#[cfg(feature = "luau-vector4")]
|
||||
let w = ffi::luaL_checknumber(state, 4) as c_float;
|
||||
|
||||
#[cfg(not(feature = "luau-vector4"))]
|
||||
ffi::lua_pushvector(state, x, y, z);
|
||||
#[cfg(feature = "luau-vector4")]
|
||||
ffi::lua_pushvector(state, x, y, z, w);
|
||||
1
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ pub use crate::HookTriggers as LuaHookTriggers;
|
|||
|
||||
#[cfg(feature = "luau")]
|
||||
#[doc(no_inline)]
|
||||
pub use crate::{CoverageInfo as LuaCoverageInfo, VmState as LuaVmState};
|
||||
pub use crate::{CoverageInfo as LuaCoverageInfo, Vector as LuaVector, VmState as LuaVmState};
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
#[doc(no_inline)]
|
||||
|
|
|
@ -124,7 +124,7 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
|
|||
#[allow(clippy::useless_conversion)]
|
||||
Value::Number(n) => visitor.visit_f64(n.into()),
|
||||
#[cfg(feature = "luau")]
|
||||
Value::Vector(_, _, _) => self.deserialize_seq(visitor),
|
||||
Value::Vector(_) => self.deserialize_seq(visitor),
|
||||
Value::String(s) => match s.to_str() {
|
||||
Ok(s) => visitor.visit_str(s),
|
||||
Err(_) => visitor.visit_bytes(s.as_bytes()),
|
||||
|
@ -223,9 +223,9 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
|
|||
{
|
||||
match self.value {
|
||||
#[cfg(feature = "luau")]
|
||||
Value::Vector(x, y, z) => {
|
||||
Value::Vector(vec) => {
|
||||
let mut deserializer = VecDeserializer {
|
||||
vec: [x, y, z],
|
||||
vec,
|
||||
next: 0,
|
||||
options: self.options,
|
||||
visited: self.visited,
|
||||
|
@ -412,7 +412,7 @@ impl<'lua, 'de> de::SeqAccess<'de> for SeqDeserializer<'lua> {
|
|||
|
||||
#[cfg(feature = "luau")]
|
||||
struct VecDeserializer {
|
||||
vec: [f32; 3],
|
||||
vec: crate::types::Vector,
|
||||
next: usize,
|
||||
options: Options,
|
||||
visited: Rc<RefCell<FxHashSet<*const c_void>>>,
|
||||
|
@ -426,7 +426,7 @@ impl<'de> de::SeqAccess<'de> for VecDeserializer {
|
|||
where
|
||||
T: de::DeserializeSeed<'de>,
|
||||
{
|
||||
match self.vec.get(self.next) {
|
||||
match self.vec.0.get(self.next) {
|
||||
Some(&n) => {
|
||||
self.next += 1;
|
||||
let visited = Rc::clone(&self.visited);
|
||||
|
@ -439,7 +439,7 @@ impl<'de> de::SeqAccess<'de> for VecDeserializer {
|
|||
}
|
||||
|
||||
fn size_hint(&self) -> Option<usize> {
|
||||
Some(3)
|
||||
Some(crate::types::Vector::SIZE)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
100
src/serde/ser.rs
100
src/serde/ser.rs
|
@ -7,8 +7,6 @@ use crate::error::{Error, Result};
|
|||
use crate::lua::Lua;
|
||||
use crate::string::String;
|
||||
use crate::table::Table;
|
||||
use crate::types::Integer;
|
||||
use crate::util::{check_stack, StackGuard};
|
||||
use crate::value::{IntoLua, Value};
|
||||
|
||||
/// A struct for serializing Rust values into Lua values.
|
||||
|
@ -120,9 +118,9 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
|
|||
|
||||
// Associated types for keeping track of additional state while serializing
|
||||
// compound data structures like sequences and maps.
|
||||
type SerializeSeq = SerializeVec<'lua>;
|
||||
type SerializeTuple = SerializeVec<'lua>;
|
||||
type SerializeTupleStruct = SerializeVec<'lua>;
|
||||
type SerializeSeq = SerializeSeq<'lua>;
|
||||
type SerializeTuple = SerializeSeq<'lua>;
|
||||
type SerializeTupleStruct = SerializeSeq<'lua>;
|
||||
type SerializeTupleVariant = SerializeTupleVariant<'lua>;
|
||||
type SerializeMap = SerializeMap<'lua>;
|
||||
type SerializeStruct = SerializeMap<'lua>;
|
||||
|
@ -240,8 +238,7 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
|
|||
if self.options.set_array_metatable {
|
||||
table.set_metatable(Some(self.lua.array_metatable()));
|
||||
}
|
||||
let options = self.options;
|
||||
Ok(SerializeVec { table, options })
|
||||
Ok(SerializeSeq::new(table, self.options))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -252,9 +249,14 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
|
|||
#[inline]
|
||||
fn serialize_tuple_struct(
|
||||
self,
|
||||
_name: &'static str,
|
||||
name: &'static str,
|
||||
len: usize,
|
||||
) -> Result<Self::SerializeTupleStruct> {
|
||||
#[cfg(feature = "luau")]
|
||||
if name == "Vector" && len == crate::types::Vector::SIZE {
|
||||
return Ok(SerializeSeq::new_vector(self.lua, self.options));
|
||||
}
|
||||
_ = name;
|
||||
self.serialize_seq(Some(len))
|
||||
}
|
||||
|
||||
|
@ -305,12 +307,40 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
|
|||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub struct SerializeVec<'lua> {
|
||||
table: Table<'lua>,
|
||||
pub struct SerializeSeq<'lua> {
|
||||
lua: &'lua Lua,
|
||||
#[cfg(feature = "luau")]
|
||||
vector: Option<crate::types::Vector>,
|
||||
table: Option<Table<'lua>>,
|
||||
next: usize,
|
||||
options: Options,
|
||||
}
|
||||
|
||||
impl<'lua> ser::SerializeSeq for SerializeVec<'lua> {
|
||||
impl<'lua> SerializeSeq<'lua> {
|
||||
const fn new(table: Table<'lua>, options: Options) -> Self {
|
||||
Self {
|
||||
lua: table.0.lua,
|
||||
#[cfg(feature = "luau")]
|
||||
vector: None,
|
||||
table: Some(table),
|
||||
next: 0,
|
||||
options,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
const fn new_vector(lua: &'lua Lua, options: Options) -> Self {
|
||||
Self {
|
||||
lua,
|
||||
vector: Some(crate::types::Vector::zero()),
|
||||
table: None,
|
||||
next: 0,
|
||||
options,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> ser::SerializeSeq for SerializeSeq<'lua> {
|
||||
type Ok = Value<'lua>;
|
||||
type Error = Error;
|
||||
|
||||
|
@ -318,35 +348,19 @@ impl<'lua> ser::SerializeSeq for SerializeVec<'lua> {
|
|||
where
|
||||
T: Serialize + ?Sized,
|
||||
{
|
||||
let lua = self.table.0.lua;
|
||||
let state = lua.state();
|
||||
let value = lua.to_value_with(value, self.options)?;
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 4)?;
|
||||
|
||||
lua.push_ref(&self.table.0);
|
||||
lua.push_value(value)?;
|
||||
if lua.unlikely_memory_error() {
|
||||
let len = ffi::lua_rawlen(state, -2) as Integer;
|
||||
ffi::lua_rawseti(state, -2, len + 1);
|
||||
ffi::lua_pop(state, 1);
|
||||
Ok(())
|
||||
} else {
|
||||
protect_lua!(state, 2, 0, fn(state) {
|
||||
let len = ffi::lua_rawlen(state, -2) as Integer;
|
||||
ffi::lua_rawseti(state, -2, len + 1);
|
||||
})
|
||||
}
|
||||
}
|
||||
let value = self.lua.to_value_with(value, self.options)?;
|
||||
let table = self.table.as_ref().unwrap();
|
||||
table.raw_seti(self.next + 1, value)?;
|
||||
self.next += 1;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn end(self) -> Result<Value<'lua>> {
|
||||
Ok(Value::Table(self.table))
|
||||
Ok(Value::Table(self.table.unwrap()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> ser::SerializeTuple for SerializeVec<'lua> {
|
||||
impl<'lua> ser::SerializeTuple for SerializeSeq<'lua> {
|
||||
type Ok = Value<'lua>;
|
||||
type Error = Error;
|
||||
|
||||
|
@ -362,7 +376,7 @@ impl<'lua> ser::SerializeTuple for SerializeVec<'lua> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'lua> ser::SerializeTupleStruct for SerializeVec<'lua> {
|
||||
impl<'lua> ser::SerializeTupleStruct for SerializeSeq<'lua> {
|
||||
type Ok = Value<'lua>;
|
||||
type Error = Error;
|
||||
|
||||
|
@ -370,10 +384,22 @@ impl<'lua> ser::SerializeTupleStruct for SerializeVec<'lua> {
|
|||
where
|
||||
T: Serialize + ?Sized,
|
||||
{
|
||||
#[cfg(feature = "luau")]
|
||||
if let Some(vector) = self.vector.as_mut() {
|
||||
let value = self.lua.to_value_with(value, self.options)?;
|
||||
let value = self.lua.unpack(value)?;
|
||||
vector.0[self.next] = value;
|
||||
self.next += 1;
|
||||
return Ok(());
|
||||
}
|
||||
ser::SerializeSeq::serialize_element(self, value)
|
||||
}
|
||||
|
||||
fn end(self) -> Result<Value<'lua>> {
|
||||
#[cfg(feature = "luau")]
|
||||
if let Some(vector) = self.vector {
|
||||
return Ok(Value::Vector(vector));
|
||||
}
|
||||
ser::SerializeSeq::end(self)
|
||||
}
|
||||
}
|
||||
|
@ -394,9 +420,7 @@ impl<'lua> ser::SerializeTupleVariant for SerializeTupleVariant<'lua> {
|
|||
T: Serialize + ?Sized,
|
||||
{
|
||||
let lua = self.table.0.lua;
|
||||
let idx = self.table.raw_len() + 1;
|
||||
self.table
|
||||
.raw_insert(idx, lua.to_value_with(value, self.options)?)
|
||||
self.table.raw_push(lua.to_value_with(value, self.options)?)
|
||||
}
|
||||
|
||||
fn end(self) -> Result<Value<'lua>> {
|
||||
|
|
28
src/table.rs
28
src/table.rs
|
@ -720,6 +720,32 @@ impl<'lua> Table<'lua> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Sets element value at position `idx` without invoking metamethods.
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn raw_seti<V: IntoLua<'lua>>(&self, idx: usize, value: V) -> Result<()> {
|
||||
#[cfg(feature = "luau")]
|
||||
self.check_readonly_write()?;
|
||||
|
||||
let lua = self.0.lua;
|
||||
let state = lua.state();
|
||||
let value = value.into_lua(lua)?;
|
||||
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 5)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
lua.push_value(value)?;
|
||||
|
||||
if lua.unlikely_memory_error() {
|
||||
ffi::lua_rawseti(state, -2, idx as _);
|
||||
} else {
|
||||
protect_lua!(state, 2, 0, |state| ffi::lua_rawseti(state, -2, idx as _))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
pub(crate) fn is_array(&self) -> bool {
|
||||
let lua = self.0.lua;
|
||||
|
@ -810,7 +836,7 @@ where
|
|||
|
||||
lua.push_ref(&self.0);
|
||||
|
||||
let len = ffi::lua_rawlen(state, -1) as usize;
|
||||
let len = ffi::lua_rawlen(state, -1);
|
||||
for i in 0..len {
|
||||
ffi::lua_rawgeti(state, -1, (i + 1) as _);
|
||||
let val = lua.pop_value();
|
||||
|
|
89
src/types.rs
89
src/types.rs
|
@ -26,6 +26,9 @@ use crate::value::MultiValue;
|
|||
#[cfg(feature = "unstable")]
|
||||
use {crate::lua::LuaInner, std::marker::PhantomData};
|
||||
|
||||
#[cfg(all(feature = "luau", feature = "serialize"))]
|
||||
use serde::ser::{Serialize, SerializeTupleStruct, Serializer};
|
||||
|
||||
/// Type of Lua integer numbers.
|
||||
pub type Integer = ffi::lua_Integer;
|
||||
/// Type of Lua floating point numbers.
|
||||
|
@ -91,6 +94,92 @@ pub trait MaybeSend {}
|
|||
#[cfg(not(feature = "send"))]
|
||||
impl<T> MaybeSend for T {}
|
||||
|
||||
/// A Luau vector type.
|
||||
///
|
||||
/// By default vectors are 3-dimensional, but can be 4-dimensional
|
||||
/// if the `luau-vector4` feature is enabled.
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
#[derive(Debug, Default, Clone, Copy, PartialEq)]
|
||||
pub struct Vector(pub(crate) [f32; Self::SIZE]);
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
impl fmt::Display for Vector {
|
||||
#[rustfmt::skip]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
#[cfg(not(feature = "luau-vector4"))]
|
||||
return write!(f, "vector({}, {}, {})", self.x(), self.y(), self.z());
|
||||
#[cfg(feature = "luau-vector4")]
|
||||
return write!(f, "vector({}, {}, {}, {})", self.x(), self.y(), self.z(), self.w());
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
impl Vector {
|
||||
pub(crate) const SIZE: usize = if cfg!(feature = "luau-vector4") { 4 } else { 3 };
|
||||
|
||||
/// Creates a new vector.
|
||||
#[cfg(not(feature = "luau-vector4"))]
|
||||
pub const fn new(x: f32, y: f32, z: f32) -> Self {
|
||||
Self([x, y, z])
|
||||
}
|
||||
|
||||
/// Creates a new vector.
|
||||
#[cfg(feature = "luau-vector4")]
|
||||
pub const fn new(x: f32, y: f32, z: f32, w: f32) -> Self {
|
||||
Self([x, y, z, w])
|
||||
}
|
||||
|
||||
/// Creates a new vector with all components set to `0.0`.
|
||||
#[doc(hidden)]
|
||||
pub const fn zero() -> Self {
|
||||
Self([0.0; Self::SIZE])
|
||||
}
|
||||
|
||||
/// Returns 1st component of the vector.
|
||||
pub const fn x(&self) -> f32 {
|
||||
self.0[0]
|
||||
}
|
||||
|
||||
/// Returns 2nd component of the vector.
|
||||
pub const fn y(&self) -> f32 {
|
||||
self.0[1]
|
||||
}
|
||||
|
||||
/// Returns 3rd component of the vector.
|
||||
pub const fn z(&self) -> f32 {
|
||||
self.0[2]
|
||||
}
|
||||
|
||||
/// Returns 4th component of the vector.
|
||||
#[cfg(any(feature = "luau-vector4", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau-vector4")))]
|
||||
pub const fn w(&self) -> f32 {
|
||||
self.0[3]
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "luau", feature = "serialize"))]
|
||||
impl Serialize for Vector {
|
||||
fn serialize<S: Serializer>(&self, serializer: S) -> StdResult<S::Ok, S::Error> {
|
||||
let mut ts = serializer.serialize_tuple_struct("Vector", Self::SIZE)?;
|
||||
ts.serialize_field(&self.x())?;
|
||||
ts.serialize_field(&self.y())?;
|
||||
ts.serialize_field(&self.z())?;
|
||||
#[cfg(feature = "luau-vector4")]
|
||||
ts.serialize_field(&self.w())?;
|
||||
ts.end()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
impl PartialEq<[f32; Self::SIZE]> for Vector {
|
||||
#[inline]
|
||||
fn eq(&self, other: &[f32; Self::SIZE]) -> bool {
|
||||
self.0 == *other
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct DestructedUserdata;
|
||||
|
||||
/// An auto generated key into the Lua registry.
|
||||
|
|
14
src/value.rs
14
src/value.rs
|
@ -44,7 +44,7 @@ pub enum Value<'lua> {
|
|||
/// A Luau vector.
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
Vector(f32, f32, f32),
|
||||
Vector(crate::types::Vector),
|
||||
/// An interned string, managed by Lua.
|
||||
///
|
||||
/// Unlike Rust strings, Lua strings may not be valid UTF-8.
|
||||
|
@ -79,7 +79,7 @@ impl<'lua> Value<'lua> {
|
|||
Value::Integer(_) => "integer",
|
||||
Value::Number(_) => "number",
|
||||
#[cfg(feature = "luau")]
|
||||
Value::Vector(_, _, _) => "vector",
|
||||
Value::Vector(_) => "vector",
|
||||
Value::String(_) => "string",
|
||||
Value::Table(_) => "table",
|
||||
Value::Function(_) => "function",
|
||||
|
@ -143,7 +143,7 @@ impl<'lua> Value<'lua> {
|
|||
Value::Integer(i) => Ok(i.to_string()),
|
||||
Value::Number(n) => Ok(n.to_string()),
|
||||
#[cfg(feature = "luau")]
|
||||
Value::Vector(x, y, z) => Ok(format!("vector({x}, {y}, {z})")),
|
||||
Value::Vector(v) => Ok(v.to_string()),
|
||||
Value::String(s) => Ok(s.to_str()?.to_string()),
|
||||
Value::Table(Table(r))
|
||||
| Value::Function(Function(r))
|
||||
|
@ -218,7 +218,7 @@ impl<'lua> Value<'lua> {
|
|||
Value::Integer(i) => write!(fmt, "{i}"),
|
||||
Value::Number(n) => write!(fmt, "{n}"),
|
||||
#[cfg(feature = "luau")]
|
||||
Value::Vector(x, y, z) => write!(fmt, "vector({x}, {y}, {z})"),
|
||||
Value::Vector(v) => write!(fmt, "{v}"),
|
||||
Value::String(s) => write!(fmt, "{s:?}"),
|
||||
Value::Table(t) if recursive && !visited.contains(&t.to_pointer()) => {
|
||||
t.fmt_pretty(fmt, ident, visited)
|
||||
|
@ -249,7 +249,7 @@ impl fmt::Debug for Value<'_> {
|
|||
Value::Integer(i) => write!(fmt, "Integer({i})"),
|
||||
Value::Number(n) => write!(fmt, "Number({n})"),
|
||||
#[cfg(feature = "luau")]
|
||||
Value::Vector(x, y, z) => write!(fmt, "Vector({x}, {y}, {z})"),
|
||||
Value::Vector(v) => write!(fmt, "{v:?}"),
|
||||
Value::String(s) => write!(fmt, "String({s:?})"),
|
||||
Value::Table(t) => write!(fmt, "{t:?}"),
|
||||
Value::Function(f) => write!(fmt, "{f:?}"),
|
||||
|
@ -271,7 +271,7 @@ impl<'lua> PartialEq for Value<'lua> {
|
|||
(Value::Number(a), Value::Integer(b)) => *a == *b as Number,
|
||||
(Value::Number(a), Value::Number(b)) => *a == *b,
|
||||
#[cfg(feature = "luau")]
|
||||
(Value::Vector(x1, y1, z1), Value::Vector(x2, y2, z2)) => (x1, y1, z1) == (x2, y2, z2),
|
||||
(Value::Vector(v1), Value::Vector(v2)) => v1 == v2,
|
||||
(Value::String(a), Value::String(b)) => a == b,
|
||||
(Value::Table(a), Value::Table(b)) => a == b,
|
||||
(Value::Function(a), Value::Function(b)) => a == b,
|
||||
|
@ -303,7 +303,7 @@ impl<'lua> Serialize for Value<'lua> {
|
|||
.serialize_i64((*i).try_into().expect("cannot convert Lua Integer to i64")),
|
||||
Value::Number(n) => serializer.serialize_f64(*n),
|
||||
#[cfg(feature = "luau")]
|
||||
Value::Vector(x, y, z) => (x, y, z).serialize(serializer),
|
||||
Value::Vector(v) => v.serialize(serializer),
|
||||
Value::String(s) => s.serialize(serializer),
|
||||
Value::Table(t) => t.serialize(serializer),
|
||||
Value::UserData(ud) => ud.serialize(serializer),
|
||||
|
|
|
@ -7,7 +7,9 @@ use std::panic::{catch_unwind, AssertUnwindSafe};
|
|||
use std::sync::atomic::{AtomicU64, Ordering};
|
||||
use std::sync::Arc;
|
||||
|
||||
use mlua::{Compiler, CoverageInfo, Error, Lua, Result, Table, ThreadStatus, Value, VmState};
|
||||
use mlua::{
|
||||
Compiler, CoverageInfo, Error, Lua, Result, Table, ThreadStatus, Value, Vector, VmState,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn test_version() -> Result<()> {
|
||||
|
@ -50,13 +52,18 @@ fn test_require() -> Result<()> {
|
|||
.exec()
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "luau-vector4"))]
|
||||
#[test]
|
||||
fn test_vectors() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let v: [f32; 3] = lua.load("vector(1, 2, 3) + vector(3, 2, 1)").eval()?;
|
||||
let v: Vector = lua.load("vector(1, 2, 3) + vector(3, 2, 1)").eval()?;
|
||||
assert_eq!(v, [4.0, 4.0, 4.0]);
|
||||
|
||||
// Test conversion into Rust array
|
||||
let v: [f64; 3] = lua.load("vector(1, 2, 3)").eval()?;
|
||||
assert!(v == [1.0, 2.0, 3.0]);
|
||||
|
||||
// Test vector methods
|
||||
lua.load(
|
||||
r#"
|
||||
|
@ -83,6 +90,46 @@ fn test_vectors() -> Result<()> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau-vector4")]
|
||||
#[test]
|
||||
fn test_vectors() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let v: Vector = lua.load("vector(1, 2, 3, 4) + vector(4, 3, 2, 1)").eval()?;
|
||||
assert_eq!(v, [5.0, 5.0, 5.0, 5.0]);
|
||||
|
||||
// Test conversion into Rust array
|
||||
let v: [f64; 4] = lua.load("vector(1, 2, 3, 4)").eval()?;
|
||||
assert!(v == [1.0, 2.0, 3.0, 4.0]);
|
||||
|
||||
// Test vector methods
|
||||
lua.load(
|
||||
r#"
|
||||
local v = vector(1, 2, 3, 4)
|
||||
assert(v.x == 1)
|
||||
assert(v.y == 2)
|
||||
assert(v.z == 3)
|
||||
assert(v.w == 4)
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
|
||||
// Test vector methods (fastcall)
|
||||
lua.load(
|
||||
r#"
|
||||
local v = vector(1, 2, 3, 4)
|
||||
assert(v.x == 1)
|
||||
assert(v.y == 2)
|
||||
assert(v.z == 3)
|
||||
assert(v.w == 4)
|
||||
"#,
|
||||
)
|
||||
.set_compiler(Compiler::new().set_vector_ctor(Some("vector".to_string())))
|
||||
.exec()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_readonly_table() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
@ -254,8 +301,8 @@ fn test_coverage() -> Result<()> {
|
|||
|
||||
let f = lua
|
||||
.load(
|
||||
r#"local v = vector(1, 2, 3)
|
||||
assert(v.x == 1 and v.y == 2 and v.z == 3)
|
||||
r#"local s = "abc"
|
||||
assert(#s == 3)
|
||||
|
||||
function abc(i)
|
||||
if i < 5 then
|
||||
|
|
|
@ -145,7 +145,7 @@ fn test_serialize_failure() -> Result<(), Box<dyn StdError>> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
#[cfg(all(feature = "luau", not(feature = "luau-vector4")))]
|
||||
#[test]
|
||||
fn test_serialize_vector() -> Result<(), Box<dyn StdError>> {
|
||||
let lua = Lua::new();
|
||||
|
@ -153,7 +153,7 @@ fn test_serialize_vector() -> Result<(), Box<dyn StdError>> {
|
|||
let globals = lua.globals();
|
||||
globals.set(
|
||||
"vector",
|
||||
lua.create_function(|_, (x, y, z)| Ok(Value::Vector(x, y, z)))?,
|
||||
lua.create_function(|_, (x, y, z)| Ok(mlua::Vector::new(x, y, z)))?,
|
||||
)?;
|
||||
|
||||
let val = lua.load("{_vector = vector(1, 2, 3)}").eval::<Value>()?;
|
||||
|
@ -168,6 +168,29 @@ fn test_serialize_vector() -> Result<(), Box<dyn StdError>> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau-vector4")]
|
||||
#[test]
|
||||
fn test_serialize_vector() -> Result<(), Box<dyn StdError>> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let globals = lua.globals();
|
||||
globals.set(
|
||||
"vector",
|
||||
lua.create_function(|_, (x, y, z, w)| Ok(mlua::Vector::new(x, y, z, w)))?,
|
||||
)?;
|
||||
|
||||
let val = lua.load("{_vector = vector(1, 2, 3, 4)}").eval::<Value>()?;
|
||||
let json = serde_json::json!({
|
||||
"_vector": [1.0, 2.0, 3.0, 4.0],
|
||||
});
|
||||
assert_eq!(serde_json::to_value(&val)?, json);
|
||||
|
||||
let expected_json = lua.from_value::<serde_json::Value>(val)?;
|
||||
assert_eq!(expected_json, json);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_value_struct() -> LuaResult<()> {
|
||||
let lua = Lua::new();
|
||||
|
|
|
@ -101,11 +101,16 @@ fn test_value_to_string() -> Result<()> {
|
|||
);
|
||||
assert_eq!(Value::Integer(1).to_string()?, "1");
|
||||
assert_eq!(Value::Number(34.59).to_string()?, "34.59");
|
||||
#[cfg(feature = "luau")]
|
||||
#[cfg(all(feature = "luau", not(feature = "luau-vector4")))]
|
||||
assert_eq!(
|
||||
Value::Vector(10.0, 11.1, 12.2).to_string()?,
|
||||
Value::Vector(mlua::Vector::new(10.0, 11.1, 12.2)).to_string()?,
|
||||
"vector(10, 11.1, 12.2)"
|
||||
);
|
||||
#[cfg(feature = "luau-vector4")]
|
||||
assert_eq!(
|
||||
Value::Vector(mlua::Vector::new(10.0, 11.1, 12.2, 13.3)).to_string()?,
|
||||
"vector(10, 11.1, 12.2, 13.3)"
|
||||
);
|
||||
assert_eq!(
|
||||
Value::String(lua.create_string("hello")?).to_string()?,
|
||||
"hello"
|
||||
|
|
Loading…
Reference in New Issue