Implement roblox enum datatypes

This commit is contained in:
Filip Tibell 2023-03-15 17:43:58 +01:00
parent 709426d8ef
commit 158603d405
No known key found for this signature in database
6 changed files with 208 additions and 3 deletions

View file

@ -173,6 +173,8 @@ impl<'lua> LuaToRbxVariant<'lua> for LuaAnyUserData<'lua> {
RbxVariantType::Color3uint8 => convert::<Color3, rbx::Color3uint8>,
RbxVariantType::ColorSequence => convert::<ColorSequence, rbx::ColorSequence>,
RbxVariantType::Enum => convert::<EnumItem, rbx::Enum>,
RbxVariantType::UDim => convert::<UDim, rbx::UDim>,
RbxVariantType::UDim2 => convert::<UDim2, rbx::UDim2>,

View file

@ -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::<Vec<_>>())
});
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 }
}
}

View file

@ -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<str>,
prop_name: impl AsRef<str>,
value: u32,
) -> Option<Self> {
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<EnumItem> for RbxEnum {
fn from(v: EnumItem) -> Self {
RbxEnum::from_u32(v.value)
}
}

View file

@ -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<LuaAnyUserData> {
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::<Vec<_>>())
});
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")
}
}

View file

@ -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;

View file

@ -7,20 +7,21 @@ pub mod instance;
#[cfg(test)]
mod tests;
fn make_dt<F>(lua: &Lua, f: F) -> LuaResult<LuaTable>
fn make_dt<F>(lua: &Lua, f: F) -> LuaResult<LuaValue>
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<Vec<(&'static str, LuaTable)>> {
fn make_all_datatypes(lua: &Lua) -> LuaResult<Vec<(&'static str, LuaValue)>> {
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<Vec<(&'static str, LuaTable)>> {
("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)?)),
])
}