From 845ddd4f8012cbf85ac9f25a5973c498be554ea7 Mon Sep 17 00:00:00 2001 From: SeanOMik Date: Sat, 10 Feb 2024 22:32:50 -0500 Subject: [PATCH] Create a helper Proxy struct for retriving proxied types as function arguments --- src/table.rs | 125 +++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 122 insertions(+), 3 deletions(-) diff --git a/src/table.rs b/src/table.rs index 4aa1cd2..8633856 100755 --- a/src/table.rs +++ b/src/table.rs @@ -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; + fn from_table<'a>(lua: &'a State, table: Table<'a>) -> Result; /// Creates a Lua instance from `Self` fn as_table<'a>(&self, state: &'a State) -> Result>; } +/// A struct that wraps a Proxied value. It can be used to get +pub struct Proxy { + data: Option, +} + +impl Proxy { + 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 { + self.data + } +} + +impl From for Proxy { + 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 { + fn from_lua(lua: &'a State, val: Value<'a>) -> crate::Result { + let table = val.into_table()?; + let t = T::from_table(lua, table)?; + + Ok(Self::from(t)) + } +} + +impl<'a, T: TableProxy> AsLua<'a> for Proxy { + fn as_lua(&self, lua: &'a State) -> crate::Result> { + 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 { @@ -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, Proxy)| { + 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(()) + } } \ No newline at end of file