mirror of
https://github.com/lune-org/lune.git
synced 2024-12-12 13:00:37 +00:00
typeof now returns roblox type names
This commit is contained in:
parent
65ea0edc12
commit
151d8cc945
8 changed files with 148 additions and 42 deletions
|
@ -50,7 +50,7 @@ pub fn ensure_valid_attribute_value(value: &DomValue) -> LuaResult<()> {
|
|||
} else {
|
||||
Err(LuaError::RuntimeError(format!(
|
||||
"'{}' is not a valid attribute type",
|
||||
value.ty().variant_name()
|
||||
value.ty().variant_name().unwrap_or("???")
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -116,7 +116,7 @@ impl<'lua> LuaToDomValue<'lua> for LuaValue<'lua> {
|
|||
(LuaValue::UserData(u), d) => u.lua_to_dom_value(lua, Some(d)),
|
||||
|
||||
(v, d) => Err(DomConversionError::ToDomValue {
|
||||
to: d.variant_name(),
|
||||
to: d.variant_name().unwrap_or("???"),
|
||||
from: v.type_name(),
|
||||
detail: None,
|
||||
}),
|
||||
|
@ -154,6 +154,16 @@ macro_rules! dom_to_userdata {
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
Converts a generic lua userdata to an rbx-dom type.
|
||||
|
||||
Since the type of the userdata needs to be specified
|
||||
in an explicit manner, this macro syntax was chosen:
|
||||
|
||||
```rs
|
||||
userdata_to_dom!(value_identifier as UserdataType => DomType)
|
||||
```
|
||||
*/
|
||||
macro_rules! userdata_to_dom {
|
||||
($userdata:ident as $from_type:ty => $to_type:ty) => {
|
||||
match $userdata.borrow::<$from_type>() {
|
||||
|
@ -212,7 +222,7 @@ impl<'lua> DomValueToLua<'lua> for LuaAnyUserData<'lua> {
|
|||
|
||||
v => {
|
||||
Err(DomConversionError::FromDomValue {
|
||||
from: v.variant_name(),
|
||||
from: v.variant_name().unwrap_or("???"),
|
||||
to: "userdata",
|
||||
detail: Some("Type not supported".to_string()),
|
||||
})
|
||||
|
@ -282,7 +292,7 @@ impl<'lua> LuaToDomValue<'lua> for LuaAnyUserData<'lua> {
|
|||
|
||||
ty => {
|
||||
return Err(DomConversionError::ToDomValue {
|
||||
to: ty.variant_name(),
|
||||
to: ty.variant_name().unwrap_or("???"),
|
||||
from: "userdata",
|
||||
detail: Some("Type not supported".to_string()),
|
||||
})
|
||||
|
@ -305,11 +315,11 @@ impl<'lua> LuaToDomValue<'lua> for LuaAnyUserData<'lua> {
|
|||
value if value.is::<Enum>() => userdata_to_dom!(value as EnumItem => dom::Enum),
|
||||
value if value.is::<Faces>() => userdata_to_dom!(value as Faces => dom::Faces),
|
||||
value if value.is::<Font>() => userdata_to_dom!(value as Font => dom::Font),
|
||||
value if value.is::<Instance>() => userdata_to_dom!(value as Instance => dom::Ref),
|
||||
value if value.is::<NumberRange>() => userdata_to_dom!(value as NumberRange => dom::NumberRange),
|
||||
value if value.is::<NumberSequence>() => userdata_to_dom!(value as NumberSequence => dom::NumberSequence),
|
||||
value if value.is::<Ray>() => userdata_to_dom!(value as Ray => dom::Ray),
|
||||
value if value.is::<Rect>() => userdata_to_dom!(value as Rect => dom::Rect),
|
||||
value if value.is::<Instance>() => userdata_to_dom!(value as Instance => dom::Ref),
|
||||
value if value.is::<Region3>() => userdata_to_dom!(value as Region3 => dom::Region3),
|
||||
value if value.is::<Region3int16>() => userdata_to_dom!(value as Region3int16 => dom::Region3int16),
|
||||
value if value.is::<UDim>() => userdata_to_dom!(value as UDim => dom::UDim),
|
||||
|
|
|
@ -1,13 +1,17 @@
|
|||
use mlua::prelude::*;
|
||||
|
||||
use crate::instance::Instance;
|
||||
|
||||
use super::*;
|
||||
|
||||
pub(super) trait DomValueExt {
|
||||
fn variant_name(&self) -> &'static str;
|
||||
fn variant_name(&self) -> Option<&'static str>;
|
||||
}
|
||||
|
||||
impl DomValueExt for DomType {
|
||||
fn variant_name(&self) -> &'static str {
|
||||
fn variant_name(&self) -> Option<&'static str> {
|
||||
use DomType::*;
|
||||
match self {
|
||||
Some(match self {
|
||||
Axes => "Axes",
|
||||
BinaryString => "BinaryString",
|
||||
Bool => "Bool",
|
||||
|
@ -40,13 +44,54 @@ impl DomValueExt for DomType {
|
|||
Vector3 => "Vector3",
|
||||
Vector3int16 => "Vector3int16",
|
||||
OptionalCFrame => "OptionalCFrame",
|
||||
_ => "?",
|
||||
}
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl DomValueExt for DomValue {
|
||||
fn variant_name(&self) -> &'static str {
|
||||
fn variant_name(&self) -> Option<&'static str> {
|
||||
self.ty().variant_name()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait RobloxUserdataTypenameExt {
|
||||
fn roblox_type_name(&self) -> Option<&'static str>;
|
||||
}
|
||||
|
||||
impl<'lua> RobloxUserdataTypenameExt for LuaAnyUserData<'lua> {
|
||||
#[rustfmt::skip]
|
||||
fn roblox_type_name(&self) -> Option<&'static str> {
|
||||
use super::types::*;
|
||||
|
||||
Some(match self {
|
||||
value if value.is::<Axes>() => "Axes",
|
||||
value if value.is::<BrickColor>() => "BrickColor",
|
||||
value if value.is::<CFrame>() => "CFrame",
|
||||
value if value.is::<Color3>() => "Color3",
|
||||
value if value.is::<ColorSequence>() => "ColorSequence",
|
||||
value if value.is::<ColorSequenceKeypoint>() => "ColorSequenceKeypoint",
|
||||
value if value.is::<Enums>() => "Enums",
|
||||
value if value.is::<Enum>() => "Enum",
|
||||
value if value.is::<EnumItem>() => "EnumItem",
|
||||
value if value.is::<Faces>() => "Faces",
|
||||
value if value.is::<Font>() => "Font",
|
||||
value if value.is::<Instance>() => "Instance",
|
||||
value if value.is::<NumberRange>() => "NumberRange",
|
||||
value if value.is::<NumberSequence>() => "NumberSequence",
|
||||
value if value.is::<NumberSequenceKeypoint>() => "NumberSequenceKeypoint",
|
||||
value if value.is::<PhysicalProperties>() => "PhysicalProperties",
|
||||
value if value.is::<Ray>() => "Ray",
|
||||
value if value.is::<Rect>() => "Rect",
|
||||
value if value.is::<Region3>() => "Region3",
|
||||
value if value.is::<Region3int16>() => "Region3int16",
|
||||
value if value.is::<UDim>() => "UDim",
|
||||
value if value.is::<UDim2>() => "UDim2",
|
||||
value if value.is::<Vector2>() => "Vector2",
|
||||
value if value.is::<Vector2int16>() => "Vector2int16",
|
||||
value if value.is::<Vector3>() => "Vector3",
|
||||
value if value.is::<Vector3int16>() => "Vector3int16",
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,16 +48,11 @@ pub fn create(lua: &'static Lua) -> LuaResult<LuaTable<'static>> {
|
|||
.build_readonly()?,
|
||||
)?
|
||||
.into_function()?;
|
||||
// We want the task scheduler to be transparent,
|
||||
// but it does not return real lua threads, so
|
||||
// we need to override some globals to fake it
|
||||
let globals = lua.globals();
|
||||
globals.set("type", lua.create_function(proxy_type)?)?;
|
||||
globals.set("typeof", lua.create_function(proxy_typeof)?)?;
|
||||
// Functions in the built-in coroutine library also need to be
|
||||
// replaced, these are a bit different than the ones above because
|
||||
// calling resume or the function that wrap returns must return
|
||||
// whatever lua value(s) that the thread or task yielded back
|
||||
let globals = lua.globals();
|
||||
let coroutine = globals.get::<_, LuaTable>("coroutine")?;
|
||||
coroutine.set("status", lua.create_function(coroutine_status)?)?;
|
||||
coroutine.set("resume", lua.create_function(coroutine_resume)?)?;
|
||||
|
@ -98,30 +93,6 @@ fn task_delay(
|
|||
sched.schedule_blocking_after_seconds(secs, tof.into_thread(lua)?, args)
|
||||
}
|
||||
|
||||
/*
|
||||
Type getter overrides for compat with task scheduler
|
||||
*/
|
||||
|
||||
fn proxy_type<'lua>(lua: &'lua Lua, value: LuaValue<'lua>) -> LuaResult<LuaString<'lua>> {
|
||||
if let LuaValue::UserData(u) = &value {
|
||||
if u.is::<TaskReference>() {
|
||||
return lua.create_string("thread");
|
||||
}
|
||||
}
|
||||
lua.named_registry_value::<_, LuaFunction>("type")?
|
||||
.call(value)
|
||||
}
|
||||
|
||||
fn proxy_typeof<'lua>(lua: &'lua Lua, value: LuaValue<'lua>) -> LuaResult<LuaString<'lua>> {
|
||||
if let LuaValue::UserData(u) = &value {
|
||||
if u.is::<TaskReference>() {
|
||||
return lua.create_string("thread");
|
||||
}
|
||||
}
|
||||
lua.named_registry_value::<_, LuaFunction>("typeof")?
|
||||
.call(value)
|
||||
}
|
||||
|
||||
/*
|
||||
Coroutine library overrides for compat with task scheduler
|
||||
*/
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
use mlua::prelude::*;
|
||||
|
||||
use crate::lua::stdio::formatting::{format_label, pretty_format_multi_value};
|
||||
#[cfg(feature = "roblox")]
|
||||
use lune_roblox::datatypes::extension::RobloxUserdataTypenameExt;
|
||||
|
||||
use crate::lua::{
|
||||
stdio::formatting::{format_label, pretty_format_multi_value},
|
||||
task::TaskReference,
|
||||
};
|
||||
|
||||
// HACK: We need to preserve the default behavior of the
|
||||
// print and error functions, for pcall and such, which
|
||||
|
@ -42,4 +48,30 @@ pub fn error(lua: &Lua, (arg, level): (LuaValue, Option<u32>)) -> LuaResult<()>
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn proxy_type<'lua>(lua: &'lua Lua, value: LuaValue<'lua>) -> LuaResult<LuaString<'lua>> {
|
||||
if let LuaValue::UserData(u) = &value {
|
||||
if u.is::<TaskReference>() {
|
||||
return lua.create_string("thread");
|
||||
}
|
||||
}
|
||||
lua.named_registry_value::<_, LuaFunction>("type")?
|
||||
.call(value)
|
||||
}
|
||||
|
||||
pub fn proxy_typeof<'lua>(lua: &'lua Lua, value: LuaValue<'lua>) -> LuaResult<LuaString<'lua>> {
|
||||
if let LuaValue::UserData(u) = &value {
|
||||
if u.is::<TaskReference>() {
|
||||
return lua.create_string("thread");
|
||||
}
|
||||
#[cfg(feature = "roblox")]
|
||||
{
|
||||
if let Some(type_name) = u.roblox_type_name() {
|
||||
return lua.create_string(type_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
lua.named_registry_value::<_, LuaFunction>("typeof")?
|
||||
.call(value)
|
||||
}
|
||||
|
||||
// TODO: Add an override for tostring that formats errors in a nicer way
|
||||
|
|
|
@ -37,6 +37,8 @@ pub fn create(lua: &'static Lua, args: Vec<String>) -> LuaResult<()> {
|
|||
("print", lua.create_function(top_level::print)?),
|
||||
("warn", lua.create_function(top_level::warn)?),
|
||||
("error", lua.create_function(top_level::error)?),
|
||||
("type", lua.create_function(top_level::proxy_type)?),
|
||||
("typeof", lua.create_function(top_level::proxy_typeof)?),
|
||||
];
|
||||
|
||||
// Set top-level globals
|
||||
|
|
|
@ -147,4 +147,6 @@ create_tests! {
|
|||
roblox_instance_methods_is_a: "roblox/instance/methods/IsA",
|
||||
roblox_instance_methods_is_ancestor_of: "roblox/instance/methods/IsAncestorOf",
|
||||
roblox_instance_methods_is_descendant_of: "roblox/instance/methods/IsDescendantOf",
|
||||
|
||||
roblox_misc_typeof: "roblox/misc/typeof",
|
||||
}
|
||||
|
|
44
tests/roblox/misc/typeof.luau
Normal file
44
tests/roblox/misc/typeof.luau
Normal file
|
@ -0,0 +1,44 @@
|
|||
local roblox = require("@lune/roblox") :: any
|
||||
|
||||
local TYPES_AND_VALUES = {
|
||||
Axes = roblox.Axes.new(),
|
||||
BrickColor = roblox.BrickColor.new("Really red"),
|
||||
CFrame = roblox.CFrame.new(),
|
||||
Color3 = roblox.Color3.new(0, 0, 0),
|
||||
ColorSequence = roblox.ColorSequence.new(roblox.Color3.new(0, 0, 0)),
|
||||
ColorSequenceKeypoint = roblox.ColorSequenceKeypoint.new(0, roblox.Color3.new(0, 0, 0)),
|
||||
Enums = roblox.Enum,
|
||||
Enum = roblox.Enum.KeyCode,
|
||||
EnumItem = roblox.Enum.KeyCode.Unknown,
|
||||
Faces = roblox.Faces.new(),
|
||||
Font = roblox.Font.new("Gotham"),
|
||||
NumberRange = roblox.NumberRange.new(0, 1),
|
||||
NumberSequence = roblox.NumberSequence.new(0, 1),
|
||||
NumberSequenceKeypoint = roblox.NumberSequenceKeypoint.new(0, 1),
|
||||
PhysicalProperties = roblox.PhysicalProperties.new(1, 1, 1),
|
||||
Ray = roblox.Ray.new(roblox.Vector3.zero, roblox.Vector3.one),
|
||||
Rect = roblox.Rect.new(0, 0, 0, 0),
|
||||
Region3 = roblox.Region3.new(roblox.Vector3.zero, roblox.Vector3.one),
|
||||
Region3int16 = roblox.Region3int16.new(
|
||||
roblox.Vector3int16.new(0, 0, 0),
|
||||
roblox.Vector3int16.new(1, 1, 1)
|
||||
),
|
||||
UDim = roblox.UDim.new(0, 0),
|
||||
UDim2 = roblox.UDim2.new(0, 0, 0, 0),
|
||||
Vector2 = roblox.Vector2.new(0, 0),
|
||||
Vector2int16 = roblox.Vector2int16.new(0, 0),
|
||||
Vector3 = roblox.Vector3.new(0, 0),
|
||||
Vector3int16 = roblox.Vector3int16.new(0, 0),
|
||||
}
|
||||
|
||||
for name, value in TYPES_AND_VALUES :: { [string]: any } do
|
||||
if typeof(value) ~= name then
|
||||
error(
|
||||
string.format(
|
||||
"typeof() did not return correct value!\nExpected: %s\nActual: %s",
|
||||
name,
|
||||
typeof(value)
|
||||
)
|
||||
)
|
||||
end
|
||||
end
|
Loading…
Reference in a new issue