From de0f017540422b33451e4bd236336f42e2db80d9 Mon Sep 17 00:00:00 2001 From: Filip Tibell Date: Wed, 23 Aug 2023 14:53:52 -0500 Subject: [PATCH] Refactor export tables for roblox builtin library --- src/lune/error.rs | 2 + src/lune/mod.rs | 6 +- src/roblox/datatypes/types/axes.rs | 95 ++++---- src/roblox/datatypes/types/brick_color.rs | 93 ++++---- src/roblox/datatypes/types/cframe.rs | 217 +++++++++--------- src/roblox/datatypes/types/color3.rs | 161 ++++++------- src/roblox/datatypes/types/color_sequence.rs | 92 ++++---- .../types/color_sequence_keypoint.rs | 28 ++- src/roblox/datatypes/types/faces.rs | 109 +++++---- src/roblox/datatypes/types/font.rs | 117 +++++----- src/roblox/datatypes/types/number_range.rs | 33 +-- src/roblox/datatypes/types/number_sequence.rs | 100 ++++---- .../types/number_sequence_keypoint.rs | 30 +-- .../datatypes/types/physical_properties.rs | 79 ++++--- src/roblox/datatypes/types/ray.rs | 30 ++- src/roblox/datatypes/types/rect.rs | 50 ++-- src/roblox/datatypes/types/region3.rs | 29 +-- src/roblox/datatypes/types/region3int16.rs | 30 +-- src/roblox/datatypes/types/udim.rs | 27 ++- src/roblox/datatypes/types/udim2.rs | 85 +++---- src/roblox/datatypes/types/vector2.rs | 37 +-- src/roblox/datatypes/types/vector2int16.rs | 27 ++- src/roblox/datatypes/types/vector3.rs | 137 +++++------ src/roblox/datatypes/types/vector3int16.rs | 29 ++- src/roblox/exports.rs | 68 ++++++ src/roblox/instance/mod.rs | 39 ++-- src/roblox/mod.rs | 86 ++++--- 27 files changed, 999 insertions(+), 837 deletions(-) create mode 100644 src/roblox/exports.rs diff --git a/src/lune/error.rs b/src/lune/error.rs index c83fad1..bde101a 100644 --- a/src/lune/error.rs +++ b/src/lune/error.rs @@ -16,6 +16,8 @@ pub struct LuneError { disable_colors: bool, } +// TODO: Rename this struct to "RuntimeError" instead for +// the next breaking release, it's a more fitting name impl LuneError { /** Enables colorization of the error message when formatted using the [`Display`] trait. diff --git a/src/lune/mod.rs b/src/lune/mod.rs index 63261d8..49c8a45 100644 --- a/src/lune/mod.rs +++ b/src/lune/mod.rs @@ -6,12 +6,16 @@ mod builtins; mod error; mod globals; mod scheduler; -mod util; + +pub(crate) mod util; use self::scheduler::{LuaSchedulerExt, Scheduler}; pub use error::LuneError; +// TODO: Rename this struct to "Runtime" instead for the +// next breaking release, it's a more fitting name and +// will probably be more obvious when browsing files #[derive(Debug, Clone)] pub struct Lune { lua: &'static Lua, diff --git a/src/roblox/datatypes/types/axes.rs b/src/roblox/datatypes/types/axes.rs index 74d9ba3..0db23f3 100644 --- a/src/roblox/datatypes/types/axes.rs +++ b/src/roblox/datatypes/types/axes.rs @@ -3,6 +3,8 @@ use core::fmt; use mlua::prelude::*; use rbx_dom_weak::types::Axes as DomAxes; +use crate::{lune::util::TableBuilder, roblox::exports::LuaExportsTable}; + use super::{super::*, EnumItem}; /** @@ -17,53 +19,58 @@ pub struct Axes { pub(crate) z: bool, } -impl Axes { - pub(crate) fn make_table(lua: &Lua, datatype_table: &LuaTable) -> LuaResult<()> { - datatype_table.set( - "new", - lua.create_function(|_, args: LuaMultiValue| { - let mut x = false; - let mut y = false; - let mut z = false; - let mut check = |e: &EnumItem| { - if e.parent.desc.name == "Axis" { - match &e.name { - name if name == "X" => x = true, - name if name == "Y" => y = true, - name if name == "Z" => z = true, - _ => {} - } - } else if e.parent.desc.name == "NormalId" { - match &e.name { - name if name == "Left" || name == "Right" => x = true, - name if name == "Top" || name == "Bottom" => y = true, - name if name == "Front" || name == "Back" => z = true, - _ => {} - } +impl LuaExportsTable<'_> for Axes { + const EXPORT_NAME: &'static str = "Axes"; + + fn create_exports_table(lua: &Lua) -> LuaResult { + let axes_new = |_, args: LuaMultiValue| { + let mut x = false; + let mut y = false; + let mut z = false; + + let mut check = |e: &EnumItem| { + if e.parent.desc.name == "Axis" { + match &e.name { + name if name == "X" => x = true, + name if name == "Y" => y = true, + name if name == "Z" => z = true, + _ => {} } - }; - for (index, arg) in args.into_iter().enumerate() { - if let LuaValue::UserData(u) = arg { - if let Ok(e) = u.borrow::() { - check(&e); - } else { - return Err(LuaError::RuntimeError(format!( - "Expected argument #{} to be an EnumItem, got userdata", - index - ))); - } - } else { - return Err(LuaError::RuntimeError(format!( - "Expected argument #{} to be an EnumItem, got {}", - index, - arg.type_name() - ))); + } else if e.parent.desc.name == "NormalId" { + match &e.name { + name if name == "Left" || name == "Right" => x = true, + name if name == "Top" || name == "Bottom" => y = true, + name if name == "Front" || name == "Back" => z = true, + _ => {} } } - Ok(Axes { x, y, z }) - })?, - )?; - Ok(()) + }; + + for (index, arg) in args.into_iter().enumerate() { + if let LuaValue::UserData(u) = arg { + if let Ok(e) = u.borrow::() { + check(&e); + } else { + return Err(LuaError::RuntimeError(format!( + "Expected argument #{} to be an EnumItem, got userdata", + index + ))); + } + } else { + return Err(LuaError::RuntimeError(format!( + "Expected argument #{} to be an EnumItem, got {}", + index, + arg.type_name() + ))); + } + } + + Ok(Axes { x, y, z }) + }; + + TableBuilder::new(lua)? + .with_function("new", axes_new)? + .build_readonly() } } diff --git a/src/roblox/datatypes/types/brick_color.rs b/src/roblox/datatypes/types/brick_color.rs index a6aaf60..8fa6f86 100644 --- a/src/roblox/datatypes/types/brick_color.rs +++ b/src/roblox/datatypes/types/brick_color.rs @@ -4,6 +4,8 @@ use mlua::prelude::*; use rand::seq::SliceRandom; use rbx_dom_weak::types::BrickColor as DomBrickColor; +use crate::{lune::util::TableBuilder, roblox::exports::LuaExportsTable}; + use super::{super::*, Color3}; /** @@ -20,57 +22,58 @@ pub struct BrickColor { pub(crate) rgb: (u8, u8, u8), } -impl BrickColor { - pub(crate) fn make_table(lua: &Lua, datatype_table: &LuaTable) -> LuaResult<()> { +impl LuaExportsTable<'_> for BrickColor { + const EXPORT_NAME: &'static str = "BrickColor"; + + fn create_exports_table(lua: &Lua) -> LuaResult { type ArgsNumber = u16; type ArgsName = String; type ArgsRgb = (u8, u8, u8); type ArgsColor3<'lua> = LuaUserDataRef<'lua, Color3>; - datatype_table.set( - "new", - lua.create_function(|lua, args: LuaMultiValue| { - if let Ok(number) = ArgsNumber::from_lua_multi(args.clone(), lua) { - Ok(color_from_number(number)) - } else if let Ok(name) = ArgsName::from_lua_multi(args.clone(), lua) { - Ok(color_from_name(name)) - } else if let Ok((r, g, b)) = ArgsRgb::from_lua_multi(args.clone(), lua) { - Ok(color_from_rgb(r, g, b)) - } else if let Ok(color) = ArgsColor3::from_lua_multi(args.clone(), lua) { - Ok(Self::from(*color)) - } else { - // FUTURE: Better error message here using given arg types - Err(LuaError::RuntimeError( - "Invalid arguments to constructor".to_string(), - )) - } - })?, - )?; - datatype_table.set( - "palette", - lua.create_function(|_, index: u16| { - if index == 0 { - Err(LuaError::RuntimeError("Invalid index".to_string())) - } else if let Some(number) = BRICK_COLOR_PALETTE.get((index - 1) as usize) { - Ok(color_from_number(*number)) - } else { - Err(LuaError::RuntimeError("Invalid index".to_string())) - } - })?, - )?; - datatype_table.set( - "random", - lua.create_function(|_, ()| { - let number = BRICK_COLOR_PALETTE.choose(&mut rand::thread_rng()); - Ok(color_from_number(*number.unwrap())) - })?, - )?; + + let brick_color_new = |lua, args: LuaMultiValue| { + if let Ok(number) = ArgsNumber::from_lua_multi(args.clone(), lua) { + Ok(color_from_number(number)) + } else if let Ok(name) = ArgsName::from_lua_multi(args.clone(), lua) { + Ok(color_from_name(name)) + } else if let Ok((r, g, b)) = ArgsRgb::from_lua_multi(args.clone(), lua) { + Ok(color_from_rgb(r, g, b)) + } else if let Ok(color) = ArgsColor3::from_lua_multi(args.clone(), lua) { + Ok(Self::from(*color)) + } else { + // FUTURE: Better error message here using given arg types + Err(LuaError::RuntimeError( + "Invalid arguments to constructor".to_string(), + )) + } + }; + + let brick_color_palette = |_, index: u16| { + if index == 0 { + Err(LuaError::RuntimeError("Invalid index".to_string())) + } else if let Some(number) = BRICK_COLOR_PALETTE.get((index - 1) as usize) { + Ok(color_from_number(*number)) + } else { + Err(LuaError::RuntimeError("Invalid index".to_string())) + } + }; + + let brick_color_random = |_, ()| { + let number = BRICK_COLOR_PALETTE.choose(&mut rand::thread_rng()); + Ok(color_from_number(*number.unwrap())) + }; + + let mut builder = TableBuilder::new(lua)? + .with_function("new", brick_color_new)? + .with_function("palette", brick_color_palette)? + .with_function("random", brick_color_random)?; + for (name, number) in BRICK_COLOR_CONSTRUCTORS { - datatype_table.set( - *name, - lua.create_function(|_, ()| Ok(color_from_number(*number)))?, - )?; + let f = |_, ()| Ok(color_from_number(*number)); + builder = builder.with_function(*name, f)?; } - Ok(()) + + builder.build_readonly() } } diff --git a/src/roblox/datatypes/types/cframe.rs b/src/roblox/datatypes/types/cframe.rs index 9f3a36c..80705b6 100644 --- a/src/roblox/datatypes/types/cframe.rs +++ b/src/roblox/datatypes/types/cframe.rs @@ -5,6 +5,8 @@ use glam::{EulerRot, Mat4, Quat, Vec3}; use mlua::prelude::*; use rbx_dom_weak::types::{CFrame as DomCFrame, Matrix3 as DomMatrix3, Vector3 as DomVector3}; +use crate::{lune::util::TableBuilder, roblox::exports::LuaExportsTable}; + use super::{super::*, Vector3}; /** @@ -35,79 +37,61 @@ impl CFrame { fn inverse(&self) -> Self { Self(self.0.inverse()) } +} + +impl LuaExportsTable<'_> for CFrame { + const EXPORT_NAME: &'static str = "CFrame"; + + fn create_exports_table(lua: &Lua) -> LuaResult { + let cframe_angles = |_, (rx, ry, rz): (f32, f32, f32)| { + Ok(CFrame(Mat4::from_euler(EulerRot::XYZ, rx, ry, rz))) + }; + + let cframe_from_axis_angle = + |_, (v, r): (LuaUserDataRef, f32)| Ok(CFrame(Mat4::from_axis_angle(v.0, r))); + + let cframe_from_euler_angles_xyz = |_, (rx, ry, rz): (f32, f32, f32)| { + Ok(CFrame(Mat4::from_euler(EulerRot::XYZ, rx, ry, rz))) + }; + + let cframe_from_euler_angles_yxz = |_, (rx, ry, rz): (f32, f32, f32)| { + Ok(CFrame(Mat4::from_euler(EulerRot::YXZ, ry, rx, rz))) + }; + + let cframe_from_matrix = |_, + (pos, rx, ry, rz): ( + LuaUserDataRef, + LuaUserDataRef, + LuaUserDataRef, + Option>, + )| { + Ok(CFrame(Mat4::from_cols( + rx.0.extend(0.0), + ry.0.extend(0.0), + rz.map(|r| r.0) + .unwrap_or_else(|| rx.0.cross(ry.0).normalize()) + .extend(0.0), + pos.0.extend(1.0), + ))) + }; + + let cframe_from_orientation = |_, (rx, ry, rz): (f32, f32, f32)| { + Ok(CFrame(Mat4::from_euler(EulerRot::YXZ, ry, rx, rz))) + }; + + let cframe_look_at = |_, + (from, to, up): ( + LuaUserDataRef, + LuaUserDataRef, + Option>, + )| { + Ok(CFrame(look_at( + from.0, + to.0, + up.as_deref().unwrap_or(&Vector3(Vec3::Y)).0, + ))) + }; - pub(crate) fn make_table(lua: &Lua, datatype_table: &LuaTable) -> LuaResult<()> { - // Constants - datatype_table.set("identity", CFrame(Mat4::IDENTITY))?; - // Strict args constructors - datatype_table.set( - "lookAt", - lua.create_function( - |_, - (from, to, up): ( - LuaUserDataRef, - LuaUserDataRef, - Option>, - )| { - Ok(CFrame(look_at( - from.0, - to.0, - up.as_deref().unwrap_or(&Vector3(Vec3::Y)).0, - ))) - }, - )?, - )?; - datatype_table.set( - "fromEulerAnglesXYZ", - lua.create_function(|_, (rx, ry, rz): (f32, f32, f32)| { - Ok(CFrame(Mat4::from_euler(EulerRot::XYZ, rx, ry, rz))) - })?, - )?; - datatype_table.set( - "fromEulerAnglesYXZ", - lua.create_function(|_, (rx, ry, rz): (f32, f32, f32)| { - Ok(CFrame(Mat4::from_euler(EulerRot::YXZ, ry, rx, rz))) - })?, - )?; - datatype_table.set( - "Angles", - lua.create_function(|_, (rx, ry, rz): (f32, f32, f32)| { - Ok(CFrame(Mat4::from_euler(EulerRot::XYZ, rx, ry, rz))) - })?, - )?; - datatype_table.set( - "fromOrientation", - lua.create_function(|_, (rx, ry, rz): (f32, f32, f32)| { - Ok(CFrame(Mat4::from_euler(EulerRot::YXZ, ry, rx, rz))) - })?, - )?; - datatype_table.set( - "fromAxisAngle", - lua.create_function(|_, (v, r): (LuaUserDataRef, f32)| { - Ok(CFrame(Mat4::from_axis_angle(v.0, r))) - })?, - )?; - datatype_table.set( - "fromMatrix", - lua.create_function( - |_, - (pos, rx, ry, rz): ( - LuaUserDataRef, - LuaUserDataRef, - LuaUserDataRef, - Option>, - )| { - Ok(CFrame(Mat4::from_cols( - rx.0.extend(0.0), - ry.0.extend(0.0), - rz.map(|r| r.0) - .unwrap_or_else(|| rx.0.cross(ry.0).normalize()) - .extend(0.0), - pos.0.extend(1.0), - ))) - }, - )?, - )?; // Dynamic args constructor type ArgsPos<'lua> = LuaUserDataRef<'lua, Vector3>; type ArgsLook<'lua> = ( @@ -115,48 +99,59 @@ impl CFrame { LuaUserDataRef<'lua, Vector3>, Option>, ); + type ArgsPosXYZ = (f32, f32, f32); type ArgsPosXYZQuat = (f32, f32, f32, f32, f32, f32, f32); type ArgsMatrix = (f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32); - datatype_table.set( - "new", - lua.create_function(|lua, args: LuaMultiValue| { - if args.clone().into_vec().is_empty() { - Ok(CFrame(Mat4::IDENTITY)) - } else if let Ok(pos) = ArgsPos::from_lua_multi(args.clone(), lua) { - Ok(CFrame(Mat4::from_translation(pos.0))) - } else if let Ok((from, to, up)) = ArgsLook::from_lua_multi(args.clone(), lua) { - Ok(CFrame(look_at( - from.0, - to.0, - up.as_deref().unwrap_or(&Vector3(Vec3::Y)).0, - ))) - } else if let Ok((x, y, z)) = ArgsPosXYZ::from_lua_multi(args.clone(), lua) { - Ok(CFrame(Mat4::from_translation(Vec3::new(x, y, z)))) - } else if let Ok((x, y, z, qx, qy, qz, qw)) = - ArgsPosXYZQuat::from_lua_multi(args.clone(), lua) - { - Ok(CFrame(Mat4::from_rotation_translation( - Quat::from_array([qx, qy, qz, qw]), - Vec3::new(x, y, z), - ))) - } else if let Ok((x, y, z, r00, r01, r02, r10, r11, r12, r20, r21, r22)) = - ArgsMatrix::from_lua_multi(args, lua) - { - Ok(CFrame(Mat4::from_cols_array_2d(&[ - [r00, r01, r02, 0.0], - [r10, r11, r12, 0.0], - [r20, r21, r22, 0.0], - [x, y, z, 1.0], - ]))) - } else { - // FUTURE: Better error message here using given arg types - Err(LuaError::RuntimeError( - "Invalid arguments to constructor".to_string(), - )) - } - })?, - ) + + let cframe_new = |lua, args: LuaMultiValue| { + if args.clone().into_vec().is_empty() { + Ok(CFrame(Mat4::IDENTITY)) + } else if let Ok(pos) = ArgsPos::from_lua_multi(args.clone(), lua) { + Ok(CFrame(Mat4::from_translation(pos.0))) + } else if let Ok((from, to, up)) = ArgsLook::from_lua_multi(args.clone(), lua) { + Ok(CFrame(look_at( + from.0, + to.0, + up.as_deref().unwrap_or(&Vector3(Vec3::Y)).0, + ))) + } else if let Ok((x, y, z)) = ArgsPosXYZ::from_lua_multi(args.clone(), lua) { + Ok(CFrame(Mat4::from_translation(Vec3::new(x, y, z)))) + } else if let Ok((x, y, z, qx, qy, qz, qw)) = + ArgsPosXYZQuat::from_lua_multi(args.clone(), lua) + { + Ok(CFrame(Mat4::from_rotation_translation( + Quat::from_array([qx, qy, qz, qw]), + Vec3::new(x, y, z), + ))) + } else if let Ok((x, y, z, r00, r01, r02, r10, r11, r12, r20, r21, r22)) = + ArgsMatrix::from_lua_multi(args, lua) + { + Ok(CFrame(Mat4::from_cols_array_2d(&[ + [r00, r01, r02, 0.0], + [r10, r11, r12, 0.0], + [r20, r21, r22, 0.0], + [x, y, z, 1.0], + ]))) + } else { + // FUTURE: Better error message here using given arg types + Err(LuaError::RuntimeError( + "Invalid arguments to constructor".to_string(), + )) + } + }; + + TableBuilder::new(lua)? + .with_function("Angles", cframe_angles)? + .with_value("identity", CFrame(Mat4::IDENTITY))? + .with_function("fromAxisAngle", cframe_from_axis_angle)? + .with_function("fromEulerAnglesXYZ", cframe_from_euler_angles_xyz)? + .with_function("fromEulerAnglesYXZ", cframe_from_euler_angles_yxz)? + .with_function("fromMatrix", cframe_from_matrix)? + .with_function("fromOrientation", cframe_from_orientation)? + .with_function("lookAt", cframe_look_at)? + .with_function("new", cframe_new)? + .build_readonly() } } diff --git a/src/roblox/datatypes/types/color3.rs b/src/roblox/datatypes/types/color3.rs index c96e092..8026875 100644 --- a/src/roblox/datatypes/types/color3.rs +++ b/src/roblox/datatypes/types/color3.rs @@ -5,6 +5,8 @@ use glam::Vec3; use mlua::prelude::*; use rbx_dom_weak::types::{Color3 as DomColor3, Color3uint8 as DomColor3uint8}; +use crate::{lune::util::TableBuilder, roblox::exports::LuaExportsTable}; + use super::super::*; /** @@ -22,88 +24,87 @@ pub struct Color3 { pub(crate) b: f32, } -impl Color3 { - pub(crate) fn make_table(lua: &Lua, datatype_table: &LuaTable) -> LuaResult<()> { - datatype_table.set( - "new", - lua.create_function(|_, (r, g, b): (Option, Option, Option)| { - Ok(Color3 { - r: r.unwrap_or_default(), - g: g.unwrap_or_default(), - b: b.unwrap_or_default(), - }) - })?, - )?; - datatype_table.set( - "fromRGB", - lua.create_function(|_, (r, g, b): (Option, Option, Option)| { - Ok(Color3 { - r: (r.unwrap_or_default() as f32) / 255f32, - g: (g.unwrap_or_default() as f32) / 255f32, - b: (b.unwrap_or_default() as f32) / 255f32, - }) - })?, - )?; - datatype_table.set( - "fromHSV", - lua.create_function(|_, (h, s, v): (f32, f32, f32)| { - // https://axonflux.com/handy-rgb-to-hsl-and-rgb-to-hsv-color-model-c - let i = (h * 6.0).floor(); - let f = h * 6.0 - i; - let p = v * (1.0 - s); - let q = v * (1.0 - f * s); - let t = v * (1.0 - (1.0 - f) * s); +impl LuaExportsTable<'_> for Color3 { + const EXPORT_NAME: &'static str = "Color3"; - let (r, g, b) = match (i % 6.0) as u8 { - 0 => (v, t, p), - 1 => (q, v, p), - 2 => (p, v, t), - 3 => (p, q, v), - 4 => (t, p, v), - 5 => (v, p, q), - _ => unreachable!(), - }; + fn create_exports_table(lua: &Lua) -> LuaResult { + let color3_from_rgb = |_, (r, g, b): (Option, Option, Option)| { + Ok(Color3 { + r: (r.unwrap_or_default() as f32) / 255f32, + g: (g.unwrap_or_default() as f32) / 255f32, + b: (b.unwrap_or_default() as f32) / 255f32, + }) + }; - Ok(Color3 { r, g, b }) - })?, - )?; - datatype_table.set( - "fromHex", - lua.create_function(|_, hex: String| { - let trimmed = hex.trim_start_matches('#').to_ascii_uppercase(); - let chars = if trimmed.len() == 3 { - ( - u8::from_str_radix(&trimmed[..1].repeat(2), 16), - u8::from_str_radix(&trimmed[1..2].repeat(2), 16), - u8::from_str_radix(&trimmed[2..3].repeat(2), 16), - ) - } else if trimmed.len() == 6 { - ( - u8::from_str_radix(&trimmed[..2], 16), - u8::from_str_radix(&trimmed[2..4], 16), - u8::from_str_radix(&trimmed[4..6], 16), - ) - } else { - return Err(LuaError::RuntimeError(format!( - "Hex color string must be 3 or 6 characters long, got {} character{}", - trimmed.len(), - if trimmed.len() == 1 { "" } else { "s" } - ))); - }; - match chars { - (Ok(r), Ok(g), Ok(b)) => Ok(Color3 { - r: (r as f32) / 255f32, - g: (g as f32) / 255f32, - b: (b as f32) / 255f32, - }), - _ => Err(LuaError::RuntimeError(format!( - "Hex color string '{}' contains invalid character", - trimmed - ))), - } - })?, - )?; - Ok(()) + let color3_from_hsv = |_, (h, s, v): (f32, f32, f32)| { + // https://axonflux.com/handy-rgb-to-hsl-and-rgb-to-hsv-color-model-c + let i = (h * 6.0).floor(); + let f = h * 6.0 - i; + let p = v * (1.0 - s); + let q = v * (1.0 - f * s); + let t = v * (1.0 - (1.0 - f) * s); + + let (r, g, b) = match (i % 6.0) as u8 { + 0 => (v, t, p), + 1 => (q, v, p), + 2 => (p, v, t), + 3 => (p, q, v), + 4 => (t, p, v), + 5 => (v, p, q), + _ => unreachable!(), + }; + + Ok(Color3 { r, g, b }) + }; + + let color3_from_hex = |_, hex: String| { + let trimmed = hex.trim_start_matches('#').to_ascii_uppercase(); + let chars = if trimmed.len() == 3 { + ( + u8::from_str_radix(&trimmed[..1].repeat(2), 16), + u8::from_str_radix(&trimmed[1..2].repeat(2), 16), + u8::from_str_radix(&trimmed[2..3].repeat(2), 16), + ) + } else if trimmed.len() == 6 { + ( + u8::from_str_radix(&trimmed[..2], 16), + u8::from_str_radix(&trimmed[2..4], 16), + u8::from_str_radix(&trimmed[4..6], 16), + ) + } else { + return Err(LuaError::RuntimeError(format!( + "Hex color string must be 3 or 6 characters long, got {} character{}", + trimmed.len(), + if trimmed.len() == 1 { "" } else { "s" } + ))); + }; + match chars { + (Ok(r), Ok(g), Ok(b)) => Ok(Color3 { + r: (r as f32) / 255f32, + g: (g as f32) / 255f32, + b: (b as f32) / 255f32, + }), + _ => Err(LuaError::RuntimeError(format!( + "Hex color string '{}' contains invalid character", + trimmed + ))), + } + }; + + let color3_new = |_, (r, g, b): (Option, Option, Option)| { + Ok(Color3 { + r: r.unwrap_or_default(), + g: g.unwrap_or_default(), + b: b.unwrap_or_default(), + }) + }; + + TableBuilder::new(lua)? + .with_function("fromRGB", color3_from_rgb)? + .with_function("fromHSV", color3_from_hsv)? + .with_function("fromHex", color3_from_hex)? + .with_function("new", color3_new)? + .build_readonly() } } diff --git a/src/roblox/datatypes/types/color_sequence.rs b/src/roblox/datatypes/types/color_sequence.rs index 154aebc..a8eb8ab 100644 --- a/src/roblox/datatypes/types/color_sequence.rs +++ b/src/roblox/datatypes/types/color_sequence.rs @@ -5,6 +5,8 @@ use rbx_dom_weak::types::{ ColorSequence as DomColorSequence, ColorSequenceKeypoint as DomColorSequenceKeypoint, }; +use crate::{lune::util::TableBuilder, roblox::exports::LuaExportsTable}; + use super::{super::*, Color3, ColorSequenceKeypoint}; /** @@ -17,52 +19,56 @@ pub struct ColorSequence { pub(crate) keypoints: Vec, } -impl ColorSequence { - pub(crate) fn make_table(lua: &Lua, datatype_table: &LuaTable) -> LuaResult<()> { +impl LuaExportsTable<'_> for ColorSequence { + const EXPORT_NAME: &'static str = "ColorSequence"; + + fn create_exports_table(lua: &Lua) -> LuaResult { type ArgsColor<'lua> = LuaUserDataRef<'lua, Color3>; type ArgsColors<'lua> = (LuaUserDataRef<'lua, Color3>, LuaUserDataRef<'lua, Color3>); type ArgsKeypoints<'lua> = Vec>; - datatype_table.set( - "new", - lua.create_function(|lua, args: LuaMultiValue| { - if let Ok(color) = ArgsColor::from_lua_multi(args.clone(), lua) { - Ok(ColorSequence { - keypoints: vec![ - ColorSequenceKeypoint { - time: 0.0, - color: *color, - }, - ColorSequenceKeypoint { - time: 1.0, - color: *color, - }, - ], - }) - } else if let Ok((c0, c1)) = ArgsColors::from_lua_multi(args.clone(), lua) { - Ok(ColorSequence { - keypoints: vec![ - ColorSequenceKeypoint { - time: 0.0, - color: *c0, - }, - ColorSequenceKeypoint { - time: 1.0, - color: *c1, - }, - ], - }) - } else if let Ok(keypoints) = ArgsKeypoints::from_lua_multi(args, lua) { - Ok(ColorSequence { - keypoints: keypoints.iter().map(|k| **k).collect(), - }) - } else { - // FUTURE: Better error message here using given arg types - Err(LuaError::RuntimeError( - "Invalid arguments to constructor".to_string(), - )) - } - })?, - ) + + let color_sequence_new = |lua, args: LuaMultiValue| { + if let Ok(color) = ArgsColor::from_lua_multi(args.clone(), lua) { + Ok(ColorSequence { + keypoints: vec![ + ColorSequenceKeypoint { + time: 0.0, + color: *color, + }, + ColorSequenceKeypoint { + time: 1.0, + color: *color, + }, + ], + }) + } else if let Ok((c0, c1)) = ArgsColors::from_lua_multi(args.clone(), lua) { + Ok(ColorSequence { + keypoints: vec![ + ColorSequenceKeypoint { + time: 0.0, + color: *c0, + }, + ColorSequenceKeypoint { + time: 1.0, + color: *c1, + }, + ], + }) + } else if let Ok(keypoints) = ArgsKeypoints::from_lua_multi(args, lua) { + Ok(ColorSequence { + keypoints: keypoints.iter().map(|k| **k).collect(), + }) + } else { + // FUTURE: Better error message here using given arg types + Err(LuaError::RuntimeError( + "Invalid arguments to constructor".to_string(), + )) + } + }; + + TableBuilder::new(lua)? + .with_function("new", color_sequence_new)? + .build_readonly() } } diff --git a/src/roblox/datatypes/types/color_sequence_keypoint.rs b/src/roblox/datatypes/types/color_sequence_keypoint.rs index 201bde7..a90b6e7 100644 --- a/src/roblox/datatypes/types/color_sequence_keypoint.rs +++ b/src/roblox/datatypes/types/color_sequence_keypoint.rs @@ -3,6 +3,8 @@ use core::fmt; use mlua::prelude::*; use rbx_dom_weak::types::ColorSequenceKeypoint as DomColorSequenceKeypoint; +use crate::{lune::util::TableBuilder, roblox::exports::LuaExportsTable}; + use super::{super::*, Color3}; /** @@ -16,18 +18,20 @@ pub struct ColorSequenceKeypoint { pub(crate) color: Color3, } -impl ColorSequenceKeypoint { - pub(crate) fn make_table(lua: &Lua, datatype_table: &LuaTable) -> LuaResult<()> { - datatype_table.set( - "new", - lua.create_function(|_, (time, color): (f32, LuaUserDataRef)| { - Ok(ColorSequenceKeypoint { - time, - color: *color, - }) - })?, - )?; - Ok(()) +impl LuaExportsTable<'_> for ColorSequenceKeypoint { + const EXPORT_NAME: &'static str = "ColorSequenceKeypoint"; + + fn create_exports_table(lua: &Lua) -> LuaResult { + let color_sequence_keypoint_new = |_, (time, color): (f32, LuaUserDataRef)| { + Ok(ColorSequenceKeypoint { + time, + color: *color, + }) + }; + + TableBuilder::new(lua)? + .with_function("new", color_sequence_keypoint_new)? + .build_readonly() } } diff --git a/src/roblox/datatypes/types/faces.rs b/src/roblox/datatypes/types/faces.rs index f21e248..b23ff72 100644 --- a/src/roblox/datatypes/types/faces.rs +++ b/src/roblox/datatypes/types/faces.rs @@ -3,6 +3,8 @@ use core::fmt; use mlua::prelude::*; use rbx_dom_weak::types::Faces as DomFaces; +use crate::{lune::util::TableBuilder, roblox::exports::LuaExportsTable}; + use super::{super::*, EnumItem}; /** @@ -20,59 +22,64 @@ pub struct Faces { pub(crate) front: bool, } -impl Faces { - pub(crate) fn make_table(lua: &Lua, datatype_table: &LuaTable) -> LuaResult<()> { - datatype_table.set( - "new", - lua.create_function(|_, args: LuaMultiValue| { - let mut right = false; - let mut top = false; - let mut back = false; - let mut left = false; - let mut bottom = false; - let mut front = false; - let mut check = |e: &EnumItem| { - if e.parent.desc.name == "NormalId" { - match &e.name { - name if name == "Right" => right = true, - name if name == "Top" => top = true, - name if name == "Back" => back = true, - name if name == "Left" => left = true, - name if name == "Bottom" => bottom = true, - name if name == "Front" => front = true, - _ => {} - } - } - }; - for (index, arg) in args.into_iter().enumerate() { - if let LuaValue::UserData(u) = arg { - if let Ok(e) = u.borrow::() { - check(&e); - } else { - return Err(LuaError::RuntimeError(format!( - "Expected argument #{} to be an EnumItem, got userdata", - index - ))); - } - } else { - return Err(LuaError::RuntimeError(format!( - "Expected argument #{} to be an EnumItem, got {}", - index, - arg.type_name() - ))); +impl LuaExportsTable<'_> for Faces { + const EXPORT_NAME: &'static str = "Faces"; + + fn create_exports_table(lua: &Lua) -> LuaResult { + let faces_new = |_, args: LuaMultiValue| { + let mut right = false; + let mut top = false; + let mut back = false; + let mut left = false; + let mut bottom = false; + let mut front = false; + + let mut check = |e: &EnumItem| { + if e.parent.desc.name == "NormalId" { + match &e.name { + name if name == "Right" => right = true, + name if name == "Top" => top = true, + name if name == "Back" => back = true, + name if name == "Left" => left = true, + name if name == "Bottom" => bottom = true, + name if name == "Front" => front = true, + _ => {} } } - Ok(Faces { - right, - top, - back, - left, - bottom, - front, - }) - })?, - )?; - Ok(()) + }; + + for (index, arg) in args.into_iter().enumerate() { + if let LuaValue::UserData(u) = arg { + if let Ok(e) = u.borrow::() { + check(&e); + } else { + return Err(LuaError::RuntimeError(format!( + "Expected argument #{} to be an EnumItem, got userdata", + index + ))); + } + } else { + return Err(LuaError::RuntimeError(format!( + "Expected argument #{} to be an EnumItem, got {}", + index, + arg.type_name() + ))); + } + } + + Ok(Faces { + right, + top, + back, + left, + bottom, + front, + }) + }; + + TableBuilder::new(lua)? + .with_function("new", faces_new)? + .build_readonly() } } diff --git a/src/roblox/datatypes/types/font.rs b/src/roblox/datatypes/types/font.rs index 8c5a19d..d3235d8 100644 --- a/src/roblox/datatypes/types/font.rs +++ b/src/roblox/datatypes/types/font.rs @@ -6,6 +6,8 @@ use rbx_dom_weak::types::{ Font as DomFont, FontStyle as DomFontStyle, FontWeight as DomFontWeight, }; +use crate::{lune::util::TableBuilder, roblox::exports::LuaExportsTable}; + use super::{super::*, EnumItem}; /** @@ -34,66 +36,65 @@ impl Font { cached_id: None, }) } +} - pub(crate) fn make_table(lua: &Lua, datatype_table: &LuaTable) -> LuaResult<()> { - datatype_table.set( - "new", - lua.create_function( - |_, (family, weight, style): (String, Option, Option)| { - Ok(Font { - family, - weight: weight.unwrap_or_default(), - style: style.unwrap_or_default(), - cached_id: None, - }) - }, - )?, - )?; - datatype_table.set( - "fromEnum", - lua.create_function(|_, value: LuaUserDataRef| { - if value.parent.desc.name == "Font" { - match Font::from_enum_item(&value) { - Some(props) => Ok(props), - None => Err(LuaError::RuntimeError(format!( - "Found unknown Font '{}'", - value.name - ))), - } - } else { - Err(LuaError::RuntimeError(format!( - "Expected argument #1 to be a Font, got {}", - value.parent.desc.name - ))) +impl LuaExportsTable<'_> for Font { + const EXPORT_NAME: &'static str = "Font"; + + fn create_exports_table(lua: &Lua) -> LuaResult { + let font_from_enum = |_, value: LuaUserDataRef| { + if value.parent.desc.name == "Font" { + match Font::from_enum_item(&value) { + Some(props) => Ok(props), + None => Err(LuaError::RuntimeError(format!( + "Found unknown Font '{}'", + value.name + ))), } - })?, - )?; - datatype_table.set( - "fromName", - lua.create_function( - |_, (file, weight, style): (String, Option, Option)| { - Ok(Font { - family: format!("rbxasset://fonts/families/{}.json", file), - weight: weight.unwrap_or_default(), - style: style.unwrap_or_default(), - cached_id: None, - }) - }, - )?, - )?; - datatype_table.set( - "fromId", - lua.create_function( - |_, (id, weight, style): (i32, Option, Option)| { - Ok(Font { - family: format!("rbxassetid://{}", id), - weight: weight.unwrap_or_default(), - style: style.unwrap_or_default(), - cached_id: None, - }) - }, - )?, - ) + } else { + Err(LuaError::RuntimeError(format!( + "Expected argument #1 to be a Font, got {}", + value.parent.desc.name + ))) + } + }; + + let font_from_name = + |_, (file, weight, style): (String, Option, Option)| { + Ok(Font { + family: format!("rbxasset://fonts/families/{}.json", file), + weight: weight.unwrap_or_default(), + style: style.unwrap_or_default(), + cached_id: None, + }) + }; + + let font_from_id = + |_, (id, weight, style): (i32, Option, Option)| { + Ok(Font { + family: format!("rbxassetid://{}", id), + weight: weight.unwrap_or_default(), + style: style.unwrap_or_default(), + cached_id: None, + }) + }; + + let font_new = + |_, (family, weight, style): (String, Option, Option)| { + Ok(Font { + family, + weight: weight.unwrap_or_default(), + style: style.unwrap_or_default(), + cached_id: None, + }) + }; + + TableBuilder::new(lua)? + .with_function("fromEnum", font_from_enum)? + .with_function("fromName", font_from_name)? + .with_function("fromId", font_from_id)? + .with_function("new", font_new)? + .build_readonly() } } diff --git a/src/roblox/datatypes/types/number_range.rs b/src/roblox/datatypes/types/number_range.rs index 065097d..b3b5ac9 100644 --- a/src/roblox/datatypes/types/number_range.rs +++ b/src/roblox/datatypes/types/number_range.rs @@ -3,6 +3,8 @@ use core::fmt; use mlua::prelude::*; use rbx_dom_weak::types::NumberRange as DomNumberRange; +use crate::{lune::util::TableBuilder, roblox::exports::LuaExportsTable}; + use super::super::*; /** @@ -16,20 +18,23 @@ pub struct NumberRange { pub(crate) max: f32, } -impl NumberRange { - pub(crate) fn make_table(lua: &Lua, datatype_table: &LuaTable) -> LuaResult<()> { - datatype_table.set( - "new", - lua.create_function(|_, (min, max): (f32, Option)| { - Ok(match max { - Some(max) => NumberRange { - min: min.min(max), - max: min.max(max), - }, - None => NumberRange { min, max: min }, - }) - })?, - ) +impl LuaExportsTable<'_> for NumberRange { + const EXPORT_NAME: &'static str = "NumberRange"; + + fn create_exports_table(lua: &Lua) -> LuaResult { + let number_range_new = |_, (min, max): (f32, Option)| { + Ok(match max { + Some(max) => NumberRange { + min: min.min(max), + max: min.max(max), + }, + None => NumberRange { min, max: min }, + }) + }; + + TableBuilder::new(lua)? + .with_function("new", number_range_new)? + .build_readonly() } } diff --git a/src/roblox/datatypes/types/number_sequence.rs b/src/roblox/datatypes/types/number_sequence.rs index 740a2da..f6b80bb 100644 --- a/src/roblox/datatypes/types/number_sequence.rs +++ b/src/roblox/datatypes/types/number_sequence.rs @@ -5,6 +5,8 @@ use rbx_dom_weak::types::{ NumberSequence as DomNumberSequence, NumberSequenceKeypoint as DomNumberSequenceKeypoint, }; +use crate::{lune::util::TableBuilder, roblox::exports::LuaExportsTable}; + use super::{super::*, NumberSequenceKeypoint}; /** @@ -17,56 +19,60 @@ pub struct NumberSequence { pub(crate) keypoints: Vec, } -impl NumberSequence { - pub(crate) fn make_table(lua: &Lua, datatype_table: &LuaTable) -> LuaResult<()> { +impl LuaExportsTable<'_> for NumberSequence { + const EXPORT_NAME: &'static str = "NumberSequence"; + + fn create_exports_table(lua: &Lua) -> LuaResult { type ArgsColor = f32; type ArgsColors = (f32, f32); type ArgsKeypoints<'lua> = Vec>; - datatype_table.set( - "new", - lua.create_function(|lua, args: LuaMultiValue| { - if let Ok(value) = ArgsColor::from_lua_multi(args.clone(), lua) { - Ok(NumberSequence { - keypoints: vec![ - NumberSequenceKeypoint { - time: 0.0, - value, - envelope: 0.0, - }, - NumberSequenceKeypoint { - time: 1.0, - value, - envelope: 0.0, - }, - ], - }) - } else if let Ok((v0, v1)) = ArgsColors::from_lua_multi(args.clone(), lua) { - Ok(NumberSequence { - keypoints: vec![ - NumberSequenceKeypoint { - time: 0.0, - value: v0, - envelope: 0.0, - }, - NumberSequenceKeypoint { - time: 1.0, - value: v1, - envelope: 0.0, - }, - ], - }) - } else if let Ok(keypoints) = ArgsKeypoints::from_lua_multi(args, lua) { - Ok(NumberSequence { - keypoints: keypoints.iter().map(|k| **k).collect(), - }) - } else { - // FUTURE: Better error message here using given arg types - Err(LuaError::RuntimeError( - "Invalid arguments to constructor".to_string(), - )) - } - })?, - ) + + let number_sequence_new = |lua, args: LuaMultiValue| { + if let Ok(value) = ArgsColor::from_lua_multi(args.clone(), lua) { + Ok(NumberSequence { + keypoints: vec![ + NumberSequenceKeypoint { + time: 0.0, + value, + envelope: 0.0, + }, + NumberSequenceKeypoint { + time: 1.0, + value, + envelope: 0.0, + }, + ], + }) + } else if let Ok((v0, v1)) = ArgsColors::from_lua_multi(args.clone(), lua) { + Ok(NumberSequence { + keypoints: vec![ + NumberSequenceKeypoint { + time: 0.0, + value: v0, + envelope: 0.0, + }, + NumberSequenceKeypoint { + time: 1.0, + value: v1, + envelope: 0.0, + }, + ], + }) + } else if let Ok(keypoints) = ArgsKeypoints::from_lua_multi(args, lua) { + Ok(NumberSequence { + keypoints: keypoints.iter().map(|k| **k).collect(), + }) + } else { + // FUTURE: Better error message here using given arg types + Err(LuaError::RuntimeError( + "Invalid arguments to constructor".to_string(), + )) + } + }; + + TableBuilder::new(lua)? + .with_function("new", number_sequence_new)? + .build_readonly() } } diff --git a/src/roblox/datatypes/types/number_sequence_keypoint.rs b/src/roblox/datatypes/types/number_sequence_keypoint.rs index f57d25b..be9aa1c 100644 --- a/src/roblox/datatypes/types/number_sequence_keypoint.rs +++ b/src/roblox/datatypes/types/number_sequence_keypoint.rs @@ -3,6 +3,8 @@ use core::fmt; use mlua::prelude::*; use rbx_dom_weak::types::NumberSequenceKeypoint as DomNumberSequenceKeypoint; +use crate::{lune::util::TableBuilder, roblox::exports::LuaExportsTable}; + use super::super::*; /** @@ -17,19 +19,21 @@ pub struct NumberSequenceKeypoint { pub(crate) envelope: f32, } -impl NumberSequenceKeypoint { - pub(crate) fn make_table(lua: &Lua, datatype_table: &LuaTable) -> LuaResult<()> { - datatype_table.set( - "new", - lua.create_function(|_, (time, value, envelope): (f32, f32, Option)| { - Ok(NumberSequenceKeypoint { - time, - value, - envelope: envelope.unwrap_or_default(), - }) - })?, - )?; - Ok(()) +impl LuaExportsTable<'_> for NumberSequenceKeypoint { + const EXPORT_NAME: &'static str = "NumberSequenceKeypoint"; + + fn create_exports_table(lua: &Lua) -> LuaResult { + let number_sequence_keypoint_new = |_, (time, value, envelope): (f32, f32, Option)| { + Ok(NumberSequenceKeypoint { + time, + value, + envelope: envelope.unwrap_or_default(), + }) + }; + + TableBuilder::new(lua)? + .with_function("new", number_sequence_keypoint_new)? + .build_readonly() } } diff --git a/src/roblox/datatypes/types/physical_properties.rs b/src/roblox/datatypes/types/physical_properties.rs index 482a750..198c9ce 100644 --- a/src/roblox/datatypes/types/physical_properties.rs +++ b/src/roblox/datatypes/types/physical_properties.rs @@ -3,6 +3,8 @@ use core::fmt; use mlua::prelude::*; use rbx_dom_weak::types::CustomPhysicalProperties as DomCustomPhysicalProperties; +use crate::{lune::util::TableBuilder, roblox::exports::LuaExportsTable}; + use super::{super::*, EnumItem}; /** @@ -32,51 +34,52 @@ impl PhysicalProperties { elasticity_weight: props.5, }) } +} - pub(crate) fn make_table(lua: &Lua, datatype_table: &LuaTable) -> LuaResult<()> { +impl LuaExportsTable<'_> for PhysicalProperties { + const EXPORT_NAME: &'static str = "PhysicalProperties"; + + fn create_exports_table(lua: &Lua) -> LuaResult { type ArgsMaterial<'lua> = LuaUserDataRef<'lua, EnumItem>; type ArgsNumbers = (f32, f32, f32, Option, Option); - datatype_table.set( - "new", - lua.create_function(|lua, args: LuaMultiValue| { - if let Ok(value) = ArgsMaterial::from_lua_multi(args.clone(), lua) { - if value.parent.desc.name == "Material" { - match PhysicalProperties::from_material(&value) { - Some(props) => Ok(props), - None => Err(LuaError::RuntimeError(format!( - "Found unknown Material '{}'", - value.name - ))), - } - } else { - Err(LuaError::RuntimeError(format!( - "Expected argument #1 to be a Material, got {}", - value.parent.desc.name - ))) + + let physical_properties_new = |lua, args: LuaMultiValue| { + if let Ok(value) = ArgsMaterial::from_lua_multi(args.clone(), lua) { + if value.parent.desc.name == "Material" { + match PhysicalProperties::from_material(&value) { + Some(props) => Ok(props), + None => Err(LuaError::RuntimeError(format!( + "Found unknown Material '{}'", + value.name + ))), } - } else if let Ok(( + } else { + Err(LuaError::RuntimeError(format!( + "Expected argument #1 to be a Material, got {}", + value.parent.desc.name + ))) + } + } else if let Ok((density, friction, elasticity, friction_weight, elasticity_weight)) = + ArgsNumbers::from_lua_multi(args, lua) + { + Ok(PhysicalProperties { density, friction, + friction_weight: friction_weight.unwrap_or(1.0), elasticity, - friction_weight, - elasticity_weight, - )) = ArgsNumbers::from_lua_multi(args, lua) - { - Ok(PhysicalProperties { - density, - friction, - friction_weight: friction_weight.unwrap_or(1.0), - elasticity, - elasticity_weight: elasticity_weight.unwrap_or(1.0), - }) - } else { - // FUTURE: Better error message here using given arg types - Err(LuaError::RuntimeError( - "Invalid arguments to constructor".to_string(), - )) - } - })?, - ) + elasticity_weight: elasticity_weight.unwrap_or(1.0), + }) + } else { + // FUTURE: Better error message here using given arg types + Err(LuaError::RuntimeError( + "Invalid arguments to constructor".to_string(), + )) + } + }; + + TableBuilder::new(lua)? + .with_function("new", physical_properties_new)? + .build_readonly() } } diff --git a/src/roblox/datatypes/types/ray.rs b/src/roblox/datatypes/types/ray.rs index c2958d1..68aba6e 100644 --- a/src/roblox/datatypes/types/ray.rs +++ b/src/roblox/datatypes/types/ray.rs @@ -4,6 +4,8 @@ use glam::Vec3; use mlua::prelude::*; use rbx_dom_weak::types::Ray as DomRay; +use crate::{lune::util::TableBuilder, roblox::exports::LuaExportsTable}; + use super::{super::*, Vector3}; /** @@ -26,19 +28,23 @@ impl Ray { let dot_product = lhs.dot(norm).max(0.0); self.origin + norm * dot_product } +} - pub(crate) fn make_table(lua: &Lua, datatype_table: &LuaTable) -> LuaResult<()> { - datatype_table.set( - "new", - lua.create_function( - |_, (origin, direction): (LuaUserDataRef, LuaUserDataRef)| { - Ok(Ray { - origin: origin.0, - direction: direction.0, - }) - }, - )?, - ) +impl LuaExportsTable<'_> for Ray { + const EXPORT_NAME: &'static str = "Ray"; + + fn create_exports_table(lua: &Lua) -> LuaResult { + let ray_new = + |_, (origin, direction): (LuaUserDataRef, LuaUserDataRef)| { + Ok(Ray { + origin: origin.0, + direction: direction.0, + }) + }; + + TableBuilder::new(lua)? + .with_function("new", ray_new)? + .build_readonly() } } diff --git a/src/roblox/datatypes/types/rect.rs b/src/roblox/datatypes/types/rect.rs index 315def7..b305184 100644 --- a/src/roblox/datatypes/types/rect.rs +++ b/src/roblox/datatypes/types/rect.rs @@ -5,6 +5,8 @@ use glam::Vec2; use mlua::prelude::*; use rbx_dom_weak::types::Rect as DomRect; +use crate::{lune::util::TableBuilder, roblox::exports::LuaExportsTable}; + use super::{super::*, Vector2}; /** @@ -28,33 +30,37 @@ impl Rect { } } -impl Rect { - pub(crate) fn make_table(lua: &Lua, datatype_table: &LuaTable) -> LuaResult<()> { +impl LuaExportsTable<'_> for Rect { + const EXPORT_NAME: &'static str = "Rect"; + + fn create_exports_table(lua: &Lua) -> LuaResult { type ArgsVector2s<'lua> = ( Option>, Option>, ); type ArgsNums = (Option, Option, Option, Option); - datatype_table.set( - "new", - lua.create_function(|lua, args: LuaMultiValue| { - if let Ok((min, max)) = ArgsVector2s::from_lua_multi(args.clone(), lua) { - Ok(Rect::new( - min.map(|m| *m).unwrap_or_default().0, - max.map(|m| *m).unwrap_or_default().0, - )) - } else if let Ok((x0, y0, x1, y1)) = ArgsNums::from_lua_multi(args, lua) { - let min = Vec2::new(x0.unwrap_or_default(), y0.unwrap_or_default()); - let max = Vec2::new(x1.unwrap_or_default(), y1.unwrap_or_default()); - Ok(Rect::new(min, max)) - } else { - // FUTURE: Better error message here using given arg types - Err(LuaError::RuntimeError( - "Invalid arguments to constructor".to_string(), - )) - } - })?, - ) + + let rect_new = |lua, args: LuaMultiValue| { + if let Ok((min, max)) = ArgsVector2s::from_lua_multi(args.clone(), lua) { + Ok(Rect::new( + min.map(|m| *m).unwrap_or_default().0, + max.map(|m| *m).unwrap_or_default().0, + )) + } else if let Ok((x0, y0, x1, y1)) = ArgsNums::from_lua_multi(args, lua) { + let min = Vec2::new(x0.unwrap_or_default(), y0.unwrap_or_default()); + let max = Vec2::new(x1.unwrap_or_default(), y1.unwrap_or_default()); + Ok(Rect::new(min, max)) + } else { + // FUTURE: Better error message here using given arg types + Err(LuaError::RuntimeError( + "Invalid arguments to constructor".to_string(), + )) + } + }; + + TableBuilder::new(lua)? + .with_function("new", rect_new)? + .build_readonly() } } diff --git a/src/roblox/datatypes/types/region3.rs b/src/roblox/datatypes/types/region3.rs index 16e97ce..9c9bdf0 100644 --- a/src/roblox/datatypes/types/region3.rs +++ b/src/roblox/datatypes/types/region3.rs @@ -4,6 +4,8 @@ use glam::{Mat4, Vec3}; use mlua::prelude::*; use rbx_dom_weak::types::Region3 as DomRegion3; +use crate::{lune::util::TableBuilder, roblox::exports::LuaExportsTable}; + use super::{super::*, CFrame, Vector3}; /** @@ -18,19 +20,20 @@ pub struct Region3 { pub(crate) max: Vec3, } -impl Region3 { - pub(crate) fn make_table(lua: &Lua, datatype_table: &LuaTable) -> LuaResult<()> { - datatype_table.set( - "new", - lua.create_function( - |_, (min, max): (LuaUserDataRef, LuaUserDataRef)| { - Ok(Region3 { - min: min.0, - max: max.0, - }) - }, - )?, - ) +impl LuaExportsTable<'_> for Region3 { + const EXPORT_NAME: &'static str = "Region3"; + + fn create_exports_table(lua: &Lua) -> LuaResult { + let region3_new = |_, (min, max): (LuaUserDataRef, LuaUserDataRef)| { + Ok(Region3 { + min: min.0, + max: max.0, + }) + }; + + TableBuilder::new(lua)? + .with_function("new", region3_new)? + .build_readonly() } } diff --git a/src/roblox/datatypes/types/region3int16.rs b/src/roblox/datatypes/types/region3int16.rs index 887aced..d64ffde 100644 --- a/src/roblox/datatypes/types/region3int16.rs +++ b/src/roblox/datatypes/types/region3int16.rs @@ -4,6 +4,8 @@ use glam::IVec3; use mlua::prelude::*; use rbx_dom_weak::types::Region3int16 as DomRegion3int16; +use crate::{lune::util::TableBuilder, roblox::exports::LuaExportsTable}; + use super::{super::*, Vector3int16}; /** @@ -18,19 +20,21 @@ pub struct Region3int16 { pub(crate) max: IVec3, } -impl Region3int16 { - pub(crate) fn make_table(lua: &Lua, datatype_table: &LuaTable) -> LuaResult<()> { - datatype_table.set( - "new", - lua.create_function( - |_, (min, max): (LuaUserDataRef, LuaUserDataRef)| { - Ok(Region3int16 { - min: min.0, - max: max.0, - }) - }, - )?, - ) +impl LuaExportsTable<'_> for Region3int16 { + const EXPORT_NAME: &'static str = "Region3int16"; + + fn create_exports_table(lua: &Lua) -> LuaResult { + let region3int16_new = + |_, (min, max): (LuaUserDataRef, LuaUserDataRef)| { + Ok(Region3int16 { + min: min.0, + max: max.0, + }) + }; + + TableBuilder::new(lua)? + .with_function("new", region3int16_new)? + .build_readonly() } } diff --git a/src/roblox/datatypes/types/udim.rs b/src/roblox/datatypes/types/udim.rs index 9fc4bc6..b390c0c 100644 --- a/src/roblox/datatypes/types/udim.rs +++ b/src/roblox/datatypes/types/udim.rs @@ -4,6 +4,8 @@ use std::ops; use mlua::prelude::*; use rbx_dom_weak::types::UDim as DomUDim; +use crate::{lune::util::TableBuilder, roblox::exports::LuaExportsTable}; + use super::super::*; /** @@ -21,17 +23,22 @@ impl UDim { pub(super) fn new(scale: f32, offset: i32) -> Self { Self { scale, offset } } +} - pub(crate) fn make_table(lua: &Lua, datatype_table: &LuaTable) -> LuaResult<()> { - datatype_table.set( - "new", - lua.create_function(|_, (scale, offset): (Option, Option)| { - Ok(UDim { - scale: scale.unwrap_or_default(), - offset: offset.unwrap_or_default(), - }) - })?, - ) +impl LuaExportsTable<'_> for UDim { + const EXPORT_NAME: &'static str = "UDim"; + + fn create_exports_table(lua: &Lua) -> LuaResult { + let udim_new = |_, (scale, offset): (Option, Option)| { + Ok(UDim { + scale: scale.unwrap_or_default(), + offset: offset.unwrap_or_default(), + }) + }; + + TableBuilder::new(lua)? + .with_function("new", udim_new)? + .build_readonly() } } diff --git a/src/roblox/datatypes/types/udim2.rs b/src/roblox/datatypes/types/udim2.rs index 597b980..4a41f98 100644 --- a/src/roblox/datatypes/types/udim2.rs +++ b/src/roblox/datatypes/types/udim2.rs @@ -5,6 +5,8 @@ use glam::Vec2; use mlua::prelude::*; use rbx_dom_weak::types::UDim2 as DomUDim2; +use crate::{lune::util::TableBuilder, roblox::exports::LuaExportsTable}; + use super::{super::*, UDim}; /** @@ -18,52 +20,53 @@ pub struct UDim2 { pub(crate) y: UDim, } -impl UDim2 { - pub(crate) fn make_table(lua: &Lua, datatype_table: &LuaTable) -> LuaResult<()> { - datatype_table.set( - "fromScale", - lua.create_function(|_, (x, y): (Option, Option)| { - Ok(UDim2 { - x: UDim::new(x.unwrap_or_default(), 0), - y: UDim::new(y.unwrap_or_default(), 0), - }) - })?, - )?; - datatype_table.set( - "fromOffset", - lua.create_function(|_, (x, y): (Option, Option)| { - Ok(UDim2 { - x: UDim::new(0f32, x.unwrap_or_default()), - y: UDim::new(0f32, y.unwrap_or_default()), - }) - })?, - )?; +impl LuaExportsTable<'_> for UDim2 { + const EXPORT_NAME: &'static str = "UDim2"; + + fn create_exports_table(lua: &Lua) -> LuaResult { + let udim2_from_offset = |_, (x, y): (Option, Option)| { + Ok(UDim2 { + x: UDim::new(0f32, x.unwrap_or_default()), + y: UDim::new(0f32, y.unwrap_or_default()), + }) + }; + + let udim2_from_scale = |_, (x, y): (Option, Option)| { + Ok(UDim2 { + x: UDim::new(x.unwrap_or_default(), 0), + y: UDim::new(y.unwrap_or_default(), 0), + }) + }; + type ArgsUDims<'lua> = ( Option>, Option>, ); type ArgsNums = (Option, Option, Option, Option); - datatype_table.set( - "new", - lua.create_function(|lua, args: LuaMultiValue| { - if let Ok((x, y)) = ArgsUDims::from_lua_multi(args.clone(), lua) { - Ok(UDim2 { - x: x.map(|x| *x).unwrap_or_default(), - y: y.map(|y| *y).unwrap_or_default(), - }) - } else if let Ok((sx, ox, sy, oy)) = ArgsNums::from_lua_multi(args, lua) { - Ok(UDim2 { - x: UDim::new(sx.unwrap_or_default(), ox.unwrap_or_default()), - y: UDim::new(sy.unwrap_or_default(), oy.unwrap_or_default()), - }) - } else { - // FUTURE: Better error message here using given arg types - Err(LuaError::RuntimeError( - "Invalid arguments to constructor".to_string(), - )) - } - })?, - ) + let udim2_new = |lua, args: LuaMultiValue| { + if let Ok((x, y)) = ArgsUDims::from_lua_multi(args.clone(), lua) { + Ok(UDim2 { + x: x.map(|x| *x).unwrap_or_default(), + y: y.map(|y| *y).unwrap_or_default(), + }) + } else if let Ok((sx, ox, sy, oy)) = ArgsNums::from_lua_multi(args, lua) { + Ok(UDim2 { + x: UDim::new(sx.unwrap_or_default(), ox.unwrap_or_default()), + y: UDim::new(sy.unwrap_or_default(), oy.unwrap_or_default()), + }) + } else { + // FUTURE: Better error message here using given arg types + Err(LuaError::RuntimeError( + "Invalid arguments to constructor".to_string(), + )) + } + }; + + TableBuilder::new(lua)? + .with_function("fromOffset", udim2_from_offset)? + .with_function("fromScale", udim2_from_scale)? + .with_function("new", udim2_new)? + .build_readonly() } } diff --git a/src/roblox/datatypes/types/vector2.rs b/src/roblox/datatypes/types/vector2.rs index 78a0a54..0eaad3d 100644 --- a/src/roblox/datatypes/types/vector2.rs +++ b/src/roblox/datatypes/types/vector2.rs @@ -5,6 +5,8 @@ use glam::{Vec2, Vec3}; use mlua::prelude::*; use rbx_dom_weak::types::Vector2 as DomVector2; +use crate::{lune::util::TableBuilder, roblox::exports::LuaExportsTable}; + use super::super::*; /** @@ -17,23 +19,24 @@ use super::super::*; #[derive(Debug, Clone, Copy, PartialEq, Default)] pub struct Vector2(pub Vec2); -impl Vector2 { - pub(crate) fn make_table(lua: &Lua, datatype_table: &LuaTable) -> LuaResult<()> { - // Constants - datatype_table.set("xAxis", Vector2(Vec2::X))?; - datatype_table.set("yAxis", Vector2(Vec2::Y))?; - datatype_table.set("zero", Vector2(Vec2::ZERO))?; - datatype_table.set("one", Vector2(Vec2::ONE))?; - // Constructors - datatype_table.set( - "new", - lua.create_function(|_, (x, y): (Option, Option)| { - Ok(Vector2(Vec2 { - x: x.unwrap_or_default(), - y: y.unwrap_or_default(), - })) - })?, - ) +impl LuaExportsTable<'_> for Vector2 { + const EXPORT_NAME: &'static str = "Vector2"; + + fn create_exports_table(lua: &Lua) -> LuaResult { + let vector2_new = |_, (x, y): (Option, Option)| { + Ok(Vector2(Vec2 { + x: x.unwrap_or_default(), + y: y.unwrap_or_default(), + })) + }; + + TableBuilder::new(lua)? + .with_value("xAxis", Vector2(Vec2::X))? + .with_value("yAxis", Vector2(Vec2::Y))? + .with_value("zero", Vector2(Vec2::ZERO))? + .with_value("one", Vector2(Vec2::ONE))? + .with_function("new", vector2_new)? + .build_readonly() } } diff --git a/src/roblox/datatypes/types/vector2int16.rs b/src/roblox/datatypes/types/vector2int16.rs index 2ae21c8..1193428 100644 --- a/src/roblox/datatypes/types/vector2int16.rs +++ b/src/roblox/datatypes/types/vector2int16.rs @@ -5,6 +5,8 @@ use glam::IVec2; use mlua::prelude::*; use rbx_dom_weak::types::Vector2int16 as DomVector2int16; +use crate::{lune::util::TableBuilder, roblox::exports::LuaExportsTable}; + use super::super::*; /** @@ -17,17 +19,20 @@ use super::super::*; #[derive(Debug, Clone, Copy, PartialEq)] pub struct Vector2int16(pub IVec2); -impl Vector2int16 { - pub(crate) fn make_table(lua: &Lua, datatype_table: &LuaTable) -> LuaResult<()> { - datatype_table.set( - "new", - lua.create_function(|_, (x, y): (Option, Option)| { - Ok(Vector2int16(IVec2 { - x: x.unwrap_or_default() as i32, - y: y.unwrap_or_default() as i32, - })) - })?, - ) +impl LuaExportsTable<'_> for Vector2int16 { + const EXPORT_NAME: &'static str = "Vector2int16"; + + fn create_exports_table(lua: &Lua) -> LuaResult { + let vector2int16_new = |_, (x, y): (Option, Option)| { + Ok(Vector2int16(IVec2 { + x: x.unwrap_or_default() as i32, + y: y.unwrap_or_default() as i32, + })) + }; + + TableBuilder::new(lua)? + .with_function("new", vector2int16_new)? + .build_readonly() } } diff --git a/src/roblox/datatypes/types/vector3.rs b/src/roblox/datatypes/types/vector3.rs index f275a41..a7ba383 100644 --- a/src/roblox/datatypes/types/vector3.rs +++ b/src/roblox/datatypes/types/vector3.rs @@ -5,6 +5,8 @@ use glam::Vec3; use mlua::prelude::*; use rbx_dom_weak::types::Vector3 as DomVector3; +use crate::{lune::util::TableBuilder, roblox::exports::LuaExportsTable}; + use super::{super::*, EnumItem}; /** @@ -20,74 +22,73 @@ use super::{super::*, EnumItem}; #[derive(Debug, Clone, Copy, PartialEq)] pub struct Vector3(pub Vec3); -impl Vector3 { - pub(crate) fn make_table(lua: &Lua, datatype_table: &LuaTable) -> LuaResult<()> { - // Constants - datatype_table.set("xAxis", Vector3(Vec3::X))?; - datatype_table.set("yAxis", Vector3(Vec3::Y))?; - datatype_table.set("zAxis", Vector3(Vec3::Z))?; - datatype_table.set("zero", Vector3(Vec3::ZERO))?; - datatype_table.set("one", Vector3(Vec3::ONE))?; - // Constructors - datatype_table.set( - "fromAxis", - lua.create_function(|_, normal_id: LuaUserDataRef| { - if normal_id.parent.desc.name == "Axis" { - Ok(match normal_id.name.as_str() { - "X" => Vector3(Vec3::X), - "Y" => Vector3(Vec3::Y), - "Z" => Vector3(Vec3::Z), - name => { - return Err(LuaError::RuntimeError(format!( - "Axis '{}' is not known", - name - ))) - } - }) - } else { - Err(LuaError::RuntimeError(format!( - "EnumItem must be a Axis, got {}", - normal_id.parent.desc.name - ))) - } - })?, - )?; - datatype_table.set( - "fromNormalId", - lua.create_function(|_, normal_id: LuaUserDataRef| { - if normal_id.parent.desc.name == "NormalId" { - Ok(match normal_id.name.as_str() { - "Left" => Vector3(Vec3::X), - "Top" => Vector3(Vec3::Y), - "Front" => Vector3(-Vec3::Z), - "Right" => Vector3(-Vec3::X), - "Bottom" => Vector3(-Vec3::Y), - "Back" => Vector3(Vec3::Z), - name => { - return Err(LuaError::RuntimeError(format!( - "NormalId '{}' is not known", - name - ))) - } - }) - } else { - Err(LuaError::RuntimeError(format!( - "EnumItem must be a NormalId, got {}", - normal_id.parent.desc.name - ))) - } - })?, - )?; - datatype_table.set( - "new", - lua.create_function(|_, (x, y, z): (Option, Option, Option)| { - Ok(Vector3(Vec3 { - x: x.unwrap_or_default(), - y: y.unwrap_or_default(), - z: z.unwrap_or_default(), - })) - })?, - ) +impl LuaExportsTable<'_> for Vector3 { + const EXPORT_NAME: &'static str = "Vector3"; + + fn create_exports_table(lua: &Lua) -> LuaResult { + let vector3_from_axis = |_, normal_id: LuaUserDataRef| { + if normal_id.parent.desc.name == "Axis" { + Ok(match normal_id.name.as_str() { + "X" => Vector3(Vec3::X), + "Y" => Vector3(Vec3::Y), + "Z" => Vector3(Vec3::Z), + name => { + return Err(LuaError::RuntimeError(format!( + "Axis '{}' is not known", + name + ))) + } + }) + } else { + Err(LuaError::RuntimeError(format!( + "EnumItem must be a Axis, got {}", + normal_id.parent.desc.name + ))) + } + }; + + let vector3_from_normal_id = |_, normal_id: LuaUserDataRef| { + if normal_id.parent.desc.name == "NormalId" { + Ok(match normal_id.name.as_str() { + "Left" => Vector3(Vec3::X), + "Top" => Vector3(Vec3::Y), + "Front" => Vector3(-Vec3::Z), + "Right" => Vector3(-Vec3::X), + "Bottom" => Vector3(-Vec3::Y), + "Back" => Vector3(Vec3::Z), + name => { + return Err(LuaError::RuntimeError(format!( + "NormalId '{}' is not known", + name + ))) + } + }) + } else { + Err(LuaError::RuntimeError(format!( + "EnumItem must be a NormalId, got {}", + normal_id.parent.desc.name + ))) + } + }; + + let vector3_new = |_, (x, y, z): (Option, Option, Option)| { + Ok(Vector3(Vec3 { + x: x.unwrap_or_default(), + y: y.unwrap_or_default(), + z: z.unwrap_or_default(), + })) + }; + + TableBuilder::new(lua)? + .with_value("xAxis", Vector3(Vec3::X))? + .with_value("yAxis", Vector3(Vec3::Y))? + .with_value("zAxis", Vector3(Vec3::Z))? + .with_value("zero", Vector3(Vec3::ZERO))? + .with_value("one", Vector3(Vec3::ONE))? + .with_function("fromAxis", vector3_from_axis)? + .with_function("fromNormalId", vector3_from_normal_id)? + .with_function("new", vector3_new)? + .build_readonly() } } diff --git a/src/roblox/datatypes/types/vector3int16.rs b/src/roblox/datatypes/types/vector3int16.rs index e8d6f08..d62e8ff 100644 --- a/src/roblox/datatypes/types/vector3int16.rs +++ b/src/roblox/datatypes/types/vector3int16.rs @@ -5,6 +5,8 @@ use glam::IVec3; use mlua::prelude::*; use rbx_dom_weak::types::Vector3int16 as DomVector3int16; +use crate::{lune::util::TableBuilder, roblox::exports::LuaExportsTable}; + use super::super::*; /** @@ -17,18 +19,21 @@ use super::super::*; #[derive(Debug, Clone, Copy, PartialEq)] pub struct Vector3int16(pub IVec3); -impl Vector3int16 { - pub(crate) fn make_table(lua: &Lua, datatype_table: &LuaTable) -> LuaResult<()> { - datatype_table.set( - "new", - lua.create_function(|_, (x, y, z): (Option, Option, Option)| { - Ok(Vector3int16(IVec3 { - x: x.unwrap_or_default() as i32, - y: y.unwrap_or_default() as i32, - z: z.unwrap_or_default() as i32, - })) - })?, - ) +impl LuaExportsTable<'_> for Vector3int16 { + const EXPORT_NAME: &'static str = "Vector3int16"; + + fn create_exports_table(lua: &Lua) -> LuaResult { + let vector3int16_new = |_, (x, y, z): (Option, Option, Option)| { + Ok(Vector3int16(IVec3 { + x: x.unwrap_or_default() as i32, + y: y.unwrap_or_default() as i32, + z: z.unwrap_or_default() as i32, + })) + }; + + TableBuilder::new(lua)? + .with_function("new", vector3int16_new)? + .build_readonly() } } diff --git a/src/roblox/exports.rs b/src/roblox/exports.rs new file mode 100644 index 0000000..2a8f834 --- /dev/null +++ b/src/roblox/exports.rs @@ -0,0 +1,68 @@ +use mlua::prelude::*; + +/** + Trait for any item that should be exported as part of the `roblox` built-in library. + + This may be an enum or a struct that should export constants and/or constructs. + + ### Example usage + + ```rs + use mlua::prelude::*; + + struct MyType(usize); + + impl MyType { + pub fn new(n: usize) -> Self { + Self(n) + } + } + + impl LuaExportsTable<'_> for MyType { + const EXPORT_NAME: &'static str = "MyType"; + + fn create_exports_table(lua: &Lua) -> LuaResult { + let my_type_new = |lua, n: Option| { + Self::new(n.unwrap_or_default()) + }; + + TableBuilder::new(lua)? + .with_function("new", my_type_new)? + .build_readonly() + } + } + + impl LuaUserData for MyType { + // ... + } + ``` +*/ +pub trait LuaExportsTable<'lua> { + const EXPORT_NAME: &'static str; + + fn create_exports_table(lua: &'lua Lua) -> LuaResult>; +} + +/** + Exports a single item that implements the [`LuaExportsTable`] trait. + + Returns the name of the export, as well as the export table. + + ### Example usage + + ```rs + let lua: mlua::Lua::new(); + + let (name1, table1) = export::(lua)?; + let (name2, table2) = export::(lua)?; + ``` +*/ +pub fn export<'lua, T>(lua: &'lua Lua) -> LuaResult<(&'static str, LuaValue<'lua>)> +where + T: LuaExportsTable<'lua>, +{ + Ok(( + T::EXPORT_NAME, + ::create_exports_table(lua)?.into_lua(lua)?, + )) +} diff --git a/src/roblox/instance/mod.rs b/src/roblox/instance/mod.rs index 9f99267..1640b1f 100644 --- a/src/roblox/instance/mod.rs +++ b/src/roblox/instance/mod.rs @@ -12,7 +12,11 @@ use rbx_dom_weak::{ Instance as DomInstance, InstanceBuilder as DomInstanceBuilder, WeakDom, }; -use crate::roblox::shared::instance::{class_exists, class_is_a}; +use crate::{ + lune::util::TableBuilder, + roblox::exports::LuaExportsTable, + roblox::shared::instance::{class_exists, class_is_a}, +}; pub(crate) mod base; pub(crate) mod data_model; @@ -686,21 +690,24 @@ impl Instance { } } -impl Instance { - pub(crate) fn make_table(lua: &Lua, datatype_table: &LuaTable) -> LuaResult<()> { - datatype_table.set( - "new", - lua.create_function(|lua, class_name: String| { - if class_exists(&class_name) { - Instance::new_orphaned(class_name).into_lua(lua) - } else { - Err(LuaError::RuntimeError(format!( - "Failed to create Instance - '{}' is not a valid class name", - class_name - ))) - } - })?, - ) +impl LuaExportsTable<'_> for Instance { + const EXPORT_NAME: &'static str = "Instance"; + + fn create_exports_table(lua: &Lua) -> LuaResult { + let instance_new = |lua, class_name: String| { + if class_exists(&class_name) { + Instance::new_orphaned(class_name).into_lua(lua) + } else { + Err(LuaError::RuntimeError(format!( + "Failed to create Instance - '{}' is not a valid class name", + class_name + ))) + } + }; + + TableBuilder::new(lua)? + .with_function("new", instance_new)? + .build_readonly() } } diff --git a/src/roblox/mod.rs b/src/roblox/mod.rs index b640d39..6b0f938 100644 --- a/src/roblox/mod.rs +++ b/src/roblox/mod.rs @@ -1,63 +1,59 @@ use mlua::prelude::*; -use crate::roblox::instance::Instance; +use crate::lune::util::TableBuilder; pub mod datatypes; pub mod document; pub mod instance; pub mod reflection; +pub(crate) mod exports; pub(crate) mod shared; -fn make(lua: &Lua, f: F) -> LuaResult -where - F: Fn(&Lua, &LuaTable) -> LuaResult<()>, -{ - let tab = lua.create_table()?; - f(lua, &tab)?; - tab.set_readonly(true); - Ok(LuaValue::Table(tab)) -} +use exports::export; -#[rustfmt::skip] -fn make_all_datatypes(lua: &Lua) -> LuaResult> { - use datatypes::types::*; +fn create_all_exports(lua: &Lua) -> LuaResult> { + use datatypes::types::*; + use instance::Instance; Ok(vec![ - // Datatypes - ("Axes", make(lua, Axes::make_table)?), - ("BrickColor", make(lua, BrickColor::make_table)?), - ("CFrame", make(lua, CFrame::make_table)?), - ("Color3", make(lua, Color3::make_table)?), - ("ColorSequence", make(lua, ColorSequence::make_table)?), - ("ColorSequenceKeypoint", make(lua, ColorSequenceKeypoint::make_table)?), - ("Faces", make(lua, Faces::make_table)?), - ("Font", make(lua, Font::make_table)?), - ("NumberRange", make(lua, NumberRange::make_table)?), - ("NumberSequence", make(lua, NumberSequence::make_table)?), - ("NumberSequenceKeypoint", make(lua, NumberSequenceKeypoint::make_table)?), - ("PhysicalProperties", make(lua, PhysicalProperties::make_table)?), - ("Ray", make(lua, Ray::make_table)?), - ("Rect", make(lua, Rect::make_table)?), - ("UDim", make(lua, UDim::make_table)?), - ("UDim2", make(lua, UDim2::make_table)?), - ("Region3", make(lua, Region3::make_table)?), - ("Region3int16", make(lua, Region3int16::make_table)?), - ("Vector2", make(lua, Vector2::make_table)?), - ("Vector2int16", make(lua, Vector2int16::make_table)?), - ("Vector3", make(lua, Vector3::make_table)?), - ("Vector3int16", make(lua, Vector3int16::make_table)?), - // Classes - ("Instance", make(lua, Instance::make_table)?), - // Singletons + // Datatypes + export::(lua)?, + export::(lua)?, + export::(lua)?, + export::(lua)?, + export::(lua)?, + export::(lua)?, + export::(lua)?, + export::(lua)?, + export::(lua)?, + export::(lua)?, + export::(lua)?, + export::(lua)?, + export::(lua)?, + export::(lua)?, + export::(lua)?, + export::(lua)?, + export::(lua)?, + export::(lua)?, + export::(lua)?, + export::(lua)?, + export::(lua)?, + export::(lua)?, + // Classes + export::(lua)?, + // Singletons ("Enum", Enums.into_lua(lua)?), ]) } pub fn module(lua: &Lua) -> LuaResult { - let exports = lua.create_table()?; - for (name, tab) in make_all_datatypes(lua)? { - exports.set(name, tab)?; - } - exports.set_readonly(true); - Ok(exports) + // FUTURE: We can probably create these lazily as users + // index the main exports (this return value) table and + // save some memory and startup time. The full exports + // table is quite big and probably won't get any smaller + // since we impl all roblox constructors for each datatype. + let exports = create_all_exports(lua)?; + TableBuilder::new(lua)? + .with_values(exports)? + .build_readonly() }