diff --git a/packages/lib-roblox/src/datatypes/conversion.rs b/packages/lib-roblox/src/datatypes/conversion.rs index d49d44d..4136587 100644 --- a/packages/lib-roblox/src/datatypes/conversion.rs +++ b/packages/lib-roblox/src/datatypes/conversion.rs @@ -173,6 +173,8 @@ impl<'lua> LuaToRbxVariant<'lua> for LuaAnyUserData<'lua> { RbxVariantType::Color3uint8 => convert::, RbxVariantType::ColorSequence => convert::, + RbxVariantType::Enum => convert::, + RbxVariantType::UDim => convert::, RbxVariantType::UDim2 => convert::, diff --git a/packages/lib-roblox/src/datatypes/types/enum.rs b/packages/lib-roblox/src/datatypes/types/enum.rs new file mode 100644 index 0000000..c797464 --- /dev/null +++ b/packages/lib-roblox/src/datatypes/types/enum.rs @@ -0,0 +1,64 @@ +use core::fmt; + +use mlua::prelude::*; +use rbx_reflection::EnumDescriptor; + +use super::EnumItem; + +/** + An implementation of the [Enum](https://create.roblox.com/docs/reference/engine/datatypes/Enum) Roblox datatype. + + This implements all documented properties, methods & constructors of the Enum class as of March 2023. +*/ +#[derive(Debug, Clone)] +pub struct Enum { + pub(crate) desc: &'static EnumDescriptor<'static>, +} + +impl LuaUserData for Enum { + fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) { + methods.add_method("GetEnumItems", |_, this, ()| { + Ok(this + .desc + .items + .iter() + .map(|(name, value)| EnumItem { + parent: this.clone(), + name: name.to_string(), + value: *value, + }) + .collect::>()) + }); + methods.add_meta_method(LuaMetaMethod::Index, |_, this, name: String| { + match this.desc.items.get(name.as_str()) { + Some(value) => Ok(EnumItem { + parent: this.clone(), + name: name.to_string(), + value: *value, + }), + None => Err(LuaError::RuntimeError(format!( + "The enum item '{}' does not exist for enum '{}'", + name, this.desc.name + ))), + } + }) + } +} + +impl fmt::Display for Enum { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Enum.{}", self.desc.name) + } +} + +impl PartialEq for Enum { + fn eq(&self, other: &Self) -> bool { + self.desc.name == other.desc.name + } +} + +impl From<&'static EnumDescriptor<'static>> for Enum { + fn from(value: &'static EnumDescriptor<'static>) -> Self { + Self { desc: value } + } +} diff --git a/packages/lib-roblox/src/datatypes/types/enum_item.rs b/packages/lib-roblox/src/datatypes/types/enum_item.rs new file mode 100644 index 0000000..783aec3 --- /dev/null +++ b/packages/lib-roblox/src/datatypes/types/enum_item.rs @@ -0,0 +1,86 @@ +use core::fmt; + +use mlua::prelude::*; +use rbx_dom_weak::types::Enum as RbxEnum; +use rbx_reflection::DataType as RbxDataType; + +use super::Enum; + +/** + An implementation of the [EnumItem](https://create.roblox.com/docs/reference/engine/datatypes/EnumItem) Roblox datatype. + + This implements all documented properties, methods & constructors of the EnumItem class as of March 2023. +*/ +#[derive(Debug, Clone)] +pub struct EnumItem { + pub(crate) parent: Enum, + pub(crate) name: String, + pub(crate) value: u32, +} + +impl EnumItem { + /** + Converts an instance property into an [`EnumItem`] datatype, if the property is known. + + Enums are not strongly typed which means we can not convert directly from a [`rbx_dom_weak::types::Enum`] + into an `EnumItem` without losing information about its parent [`Enum`] and the `EnumItem` name. + + This constructor exists as a shortcut to perform a [`rbx_reflection_database`] lookup for a particular + instance class and property to construct a strongly typed `EnumItem` with no loss of information. + */ + #[allow(dead_code)] + fn from_instance_property( + class_name: impl AsRef, + prop_name: impl AsRef, + value: u32, + ) -> Option { + let db = rbx_reflection_database::get(); + let prop = db + .classes + .get(class_name.as_ref())? + .properties + .get(prop_name.as_ref())?; + let prop_enum = match &prop.data_type { + RbxDataType::Enum(name) => db.enums.get(name.as_ref()), + _ => None, + }?; + let enum_name = prop_enum.items.iter().find_map(|(name, v)| { + if v == &value { + Some(name.to_string()) + } else { + None + } + })?; + Some(Self { + parent: prop_enum.into(), + name: enum_name, + value, + }) + } +} + +impl LuaUserData for EnumItem { + fn add_fields<'lua, F: LuaUserDataFields<'lua, Self>>(fields: &mut F) { + fields.add_field_method_get("Name", |_, this| Ok(this.name.clone())); + fields.add_field_method_get("Value", |_, this| Ok(this.value)); + fields.add_field_method_get("EnumType", |_, this| Ok(this.parent.clone())); + } +} + +impl fmt::Display for EnumItem { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}.{}", self.parent, self.name) + } +} + +impl PartialEq for EnumItem { + fn eq(&self, other: &Self) -> bool { + self.parent == other.parent && self.value == other.value + } +} + +impl From for RbxEnum { + fn from(v: EnumItem) -> Self { + RbxEnum::from_u32(v.value) + } +} diff --git a/packages/lib-roblox/src/datatypes/types/enums.rs b/packages/lib-roblox/src/datatypes/types/enums.rs new file mode 100644 index 0000000..6397568 --- /dev/null +++ b/packages/lib-roblox/src/datatypes/types/enums.rs @@ -0,0 +1,44 @@ +use core::fmt; + +use mlua::prelude::*; + +use super::Enum; + +/** + An implementation of the [Enums](https://create.roblox.com/docs/reference/engine/datatypes/Enums) Roblox datatype. + + This implements all documented properties, methods & constructors of the Enums class as of March 2023. +*/ +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct Enums; + +impl Enums { + pub(crate) fn make_singleton(lua: &Lua) -> LuaResult { + lua.create_userdata(Self) + } +} + +impl LuaUserData for Enums { + fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) { + methods.add_method("GetEnums", |_, _, ()| { + let db = rbx_reflection_database::get(); + Ok(db.enums.values().map(Enum::from).collect::>()) + }); + methods.add_meta_method(LuaMetaMethod::Index, |_, _, name: String| { + let db = rbx_reflection_database::get(); + match db.enums.get(name.as_str()) { + Some(desc) => Ok(Enum::from(desc)), + None => Err(LuaError::RuntimeError(format!( + "The enum '{}' does not exist", + name + ))), + } + }) + } +} + +impl fmt::Display for Enums { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Enum") + } +} diff --git a/packages/lib-roblox/src/datatypes/types/mod.rs b/packages/lib-roblox/src/datatypes/types/mod.rs index 54631e3..d9c1371 100644 --- a/packages/lib-roblox/src/datatypes/types/mod.rs +++ b/packages/lib-roblox/src/datatypes/types/mod.rs @@ -2,6 +2,9 @@ mod brick_color; mod color3; mod color_sequence; mod color_sequence_keypoint; +mod r#enum; +mod r#enum_item; +mod r#enums; mod udim; mod udim2; mod vector2; @@ -13,6 +16,9 @@ pub use brick_color::BrickColor; pub use color3::Color3; pub use color_sequence::ColorSequence; pub use color_sequence_keypoint::ColorSequenceKeypoint; +pub use r#enum::Enum; +pub use r#enum_item::EnumItem; +pub use r#enums::Enums; pub use udim::UDim; pub use udim2::UDim2; pub use vector2::Vector2; diff --git a/packages/lib-roblox/src/lib.rs b/packages/lib-roblox/src/lib.rs index 1ca677f..a687558 100644 --- a/packages/lib-roblox/src/lib.rs +++ b/packages/lib-roblox/src/lib.rs @@ -7,20 +7,21 @@ pub mod instance; #[cfg(test)] mod tests; -fn make_dt(lua: &Lua, f: F) -> LuaResult +fn make_dt(lua: &Lua, f: F) -> LuaResult where F: Fn(&Lua, &LuaTable) -> LuaResult<()>, { let tab = lua.create_table()?; f(lua, &tab)?; tab.set_readonly(true); - Ok(tab) + Ok(LuaValue::Table(tab)) } #[rustfmt::skip] -fn make_all_datatypes(lua: &Lua) -> LuaResult> { +fn make_all_datatypes(lua: &Lua) -> LuaResult> { use datatypes::types::*; Ok(vec![ + // Classes ("BrickColor", make_dt(lua, BrickColor::make_table)?), ("Color3", make_dt(lua, Color3::make_table)?), ("ColorSequence", make_dt(lua, ColorSequence::make_table)?), @@ -31,6 +32,8 @@ fn make_all_datatypes(lua: &Lua) -> LuaResult> { ("Vector2int16", make_dt(lua, Vector2int16::make_table)?), ("Vector3", make_dt(lua, Vector3::make_table)?), ("Vector3int16", make_dt(lua, Vector3int16::make_table)?), + // Singletons + ("Enum", LuaValue::UserData(Enums::make_singleton(lua)?)), ]) }