Create a helper Proxy struct for retriving proxied types as function arguments

This commit is contained in:
SeanOMik 2024-02-10 22:32:50 -05:00
parent 7dc4380d5c
commit 845ddd4f80
Signed by: SeanOMik
GPG Key ID: FEC9E2FC15235964
1 changed files with 122 additions and 3 deletions

View File

@ -1,4 +1,3 @@
use mlua_sys as lua;
use crate::{ensure_type, AsLua, FromLua, FromLuaStack, LuaRef, PushToLuaStack, Result, StackGuard, State, Value};
@ -324,14 +323,67 @@ impl<'a> AsLua<'a> for Table<'a> {
/// 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>(state: &'a State, table: Table<'a>) -> Result<Self>;
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, State, StdLibrary, Table, TableProxy};
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> {
@ -461,4 +513,71 @@ mod tests {
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(())
}
}