mirror of
https://github.com/mlua-rs/mlua
synced 2026-06-08 16:05:43 +00:00
Add Luau vector datatype support
This commit is contained in:
+37
-28
@@ -464,21 +464,33 @@ impl<'lua, T, const N: usize> FromLua<'lua> for [T; N]
|
||||
where
|
||||
T: FromLua<'lua>,
|
||||
{
|
||||
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Self> {
|
||||
if let Value::Table(table) = value {
|
||||
let vec = table.sequence_values().collect::<Result<Vec<_>>>()?;
|
||||
vec.try_into()
|
||||
.map_err(|vec: Vec<T>| Error::FromLuaConversionError {
|
||||
from: "Table",
|
||||
to: "Array",
|
||||
message: Some(format!("expected table of length {}, got {}", N, vec.len())),
|
||||
})
|
||||
} else {
|
||||
Err(Error::FromLuaConversionError {
|
||||
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"
|
||||
)),
|
||||
Value::Table(table) => {
|
||||
let vec = table.sequence_values().collect::<Result<Vec<_>>>()?;
|
||||
vec.try_into()
|
||||
.map_err(|vec: Vec<T>| Error::FromLuaConversionError {
|
||||
from: "Table",
|
||||
to: "Array",
|
||||
message: Some(format!("expected table of length {}, got {}", N, vec.len())),
|
||||
})
|
||||
}
|
||||
_ => Err(Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: "Array",
|
||||
message: Some("expected table".to_string()),
|
||||
})
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -490,16 +502,8 @@ impl<'lua, T: ToLua<'lua>> ToLua<'lua> for Box<[T]> {
|
||||
}
|
||||
|
||||
impl<'lua, T: FromLua<'lua>> FromLua<'lua> for Box<[T]> {
|
||||
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Self> {
|
||||
if let Value::Table(table) = value {
|
||||
table.sequence_values().collect()
|
||||
} else {
|
||||
Err(Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: "Box<[T]>",
|
||||
message: Some("expected table".to_string()),
|
||||
})
|
||||
}
|
||||
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Self> {
|
||||
Ok(Vec::<T>::from_lua(value, lua)?.into_boxed_slice())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -510,15 +514,20 @@ impl<'lua, T: ToLua<'lua>> ToLua<'lua> for Vec<T> {
|
||||
}
|
||||
|
||||
impl<'lua, T: FromLua<'lua>> FromLua<'lua> for Vec<T> {
|
||||
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Self> {
|
||||
if let Value::Table(table) = value {
|
||||
table.sequence_values().collect()
|
||||
} else {
|
||||
Err(Error::FromLuaConversionError {
|
||||
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(),
|
||||
to: "Vec",
|
||||
message: Some("expected table".to_string()),
|
||||
})
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+14
@@ -1972,6 +1972,11 @@ impl Lua {
|
||||
ffi::lua_pushnumber(self.state, n);
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
Value::Vector(x, y, z) => {
|
||||
ffi::lua_pushvector(self.state, x, y, z);
|
||||
}
|
||||
|
||||
Value::String(s) => {
|
||||
self.push_ref(&s.0);
|
||||
}
|
||||
@@ -2033,6 +2038,15 @@ impl Lua {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
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));
|
||||
ffi::lua_pop(state, 1);
|
||||
vec
|
||||
}
|
||||
|
||||
ffi::LUA_TSTRING => Value::String(String(self.pop_ref())),
|
||||
|
||||
ffi::LUA_TTABLE => Value::Table(Table(self.pop_ref())),
|
||||
|
||||
@@ -123,6 +123,8 @@ 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::String(s) => match s.to_str() {
|
||||
Ok(s) => visitor.visit_str(s),
|
||||
Err(_) => visitor.visit_bytes(s.as_bytes()),
|
||||
@@ -214,6 +216,16 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
|
||||
V: de::Visitor<'de>,
|
||||
{
|
||||
match self.value {
|
||||
#[cfg(feature = "luau")]
|
||||
Value::Vector(x, y, z) => {
|
||||
let mut deserializer = VecDeserializer {
|
||||
vec: [x, y, z],
|
||||
next: 0,
|
||||
options: self.options,
|
||||
visited: self.visited,
|
||||
};
|
||||
visitor.visit_seq(&mut deserializer)
|
||||
}
|
||||
Value::Table(t) => {
|
||||
let _guard = RecursionGuard::new(&t, &self.visited);
|
||||
|
||||
@@ -352,6 +364,39 @@ impl<'lua, 'de> de::SeqAccess<'de> for SeqDeserializer<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
struct VecDeserializer {
|
||||
vec: [f32; 3],
|
||||
next: usize,
|
||||
options: Options,
|
||||
visited: Rc<RefCell<FxHashSet<*const c_void>>>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
impl<'de> de::SeqAccess<'de> for VecDeserializer {
|
||||
type Error = Error;
|
||||
|
||||
fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>>
|
||||
where
|
||||
T: de::DeserializeSeed<'de>,
|
||||
{
|
||||
match self.vec.get(self.next) {
|
||||
Some(&n) => {
|
||||
self.next += 1;
|
||||
let visited = Rc::clone(&self.visited);
|
||||
let deserializer =
|
||||
Deserializer::from_parts(Value::Number(n as _), self.options, visited);
|
||||
seed.deserialize(deserializer).map(Some)
|
||||
}
|
||||
None => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> Option<usize> {
|
||||
Some(3)
|
||||
}
|
||||
}
|
||||
|
||||
struct MapDeserializer<'lua> {
|
||||
pairs: TablePairs<'lua, Value<'lua>, Value<'lua>>,
|
||||
value: Option<Value<'lua>>,
|
||||
|
||||
@@ -965,6 +965,13 @@ pub(crate) unsafe fn to_string(state: *mut ffi::lua_State, index: c_int) -> Stri
|
||||
i.to_string()
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "luau")]
|
||||
ffi::LUA_TVECTOR => {
|
||||
let v = ffi::lua_tovector(state, index);
|
||||
mlua_debug_assert!(!v.is_null(), "vector is null");
|
||||
let (x, y, z) = (*v, *v.add(1), *v.add(2));
|
||||
format!("vector({},{},{})", x, y, z)
|
||||
}
|
||||
ffi::LUA_TSTRING => {
|
||||
let mut size = 0;
|
||||
// This will not trigger a 'm' error, because the reference is guaranteed to be of
|
||||
|
||||
@@ -34,6 +34,9 @@ pub enum Value<'lua> {
|
||||
Integer(Integer),
|
||||
/// A floating point number.
|
||||
Number(Number),
|
||||
/// A Luau vector.
|
||||
#[cfg(feature = "luau")]
|
||||
Vector(f32, f32, f32),
|
||||
/// An interned string, managed by Lua.
|
||||
///
|
||||
/// Unlike Rust strings, Lua strings may not be valid UTF-8.
|
||||
@@ -61,6 +64,8 @@ impl<'lua> Value<'lua> {
|
||||
Value::LightUserData(_) => "lightuserdata",
|
||||
Value::Integer(_) => "integer",
|
||||
Value::Number(_) => "number",
|
||||
#[cfg(feature = "luau")]
|
||||
Value::Vector(_, _, _) => "vector",
|
||||
Value::String(_) => "string",
|
||||
Value::Table(_) => "table",
|
||||
Value::Function(_) => "function",
|
||||
@@ -99,6 +104,8 @@ impl<'lua> PartialEq for Value<'lua> {
|
||||
(Value::Integer(a), Value::Number(b)) => *a as Number == *b,
|
||||
(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::String(a), Value::String(b)) => a == b,
|
||||
(Value::Table(a), Value::Table(b)) => a == b,
|
||||
(Value::Function(a), Value::Function(b)) => a == b,
|
||||
@@ -130,6 +137,8 @@ impl<'lua> Serialize for Value<'lua> {
|
||||
.serialize_i64((*i).try_into().expect("cannot convert lua_Integer to i64")),
|
||||
#[allow(clippy::useless_conversion)]
|
||||
Value::Number(n) => serializer.serialize_f64(*n),
|
||||
#[cfg(feature = "luau")]
|
||||
Value::Vector(x, y, z) => (x, y, z).serialize(serializer),
|
||||
Value::String(s) => s.serialize(serializer),
|
||||
Value::Table(t) => t.serialize(serializer),
|
||||
Value::UserData(ud) => ud.serialize(serializer),
|
||||
|
||||
+19
-1
@@ -3,7 +3,7 @@
|
||||
use std::env;
|
||||
use std::fs;
|
||||
|
||||
use mlua::{Lua, Result};
|
||||
use mlua::{Lua, Result, Value};
|
||||
|
||||
#[test]
|
||||
fn test_require() -> Result<()> {
|
||||
@@ -29,3 +29,21 @@ fn test_require() -> Result<()> {
|
||||
)
|
||||
.exec()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vectors() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let globals = lua.globals();
|
||||
globals.set(
|
||||
"vector",
|
||||
lua.create_function(|_, (x, y, z)| Ok(Value::Vector(x, y, z)))?,
|
||||
)?;
|
||||
|
||||
let v: [f32; 3] = lua
|
||||
.load("return vector(1, 2, 3) + vector(3, 2, 1)")
|
||||
.eval()?;
|
||||
assert_eq!(v, [4.0, 4.0, 4.0]);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -144,6 +144,29 @@ fn test_serialize_failure() -> Result<(), Box<dyn std::error::Error>> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
#[test]
|
||||
fn test_serialize_vector() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let globals = lua.globals();
|
||||
globals.set(
|
||||
"vector",
|
||||
lua.create_function(|_, (x, y, z)| Ok(Value::Vector(x, y, z)))?,
|
||||
)?;
|
||||
|
||||
let val = lua.load("{_vector = vector(1, 2, 3)}").eval::<Value>()?;
|
||||
let json = serde_json::json!({
|
||||
"_vector": [1.0, 2.0, 3.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();
|
||||
|
||||
Reference in New Issue
Block a user