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 {
|
} else {
|
||||||
Err(LuaError::RuntimeError(format!(
|
Err(LuaError::RuntimeError(format!(
|
||||||
"'{}' is not a valid attribute type",
|
"'{}' 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)),
|
(LuaValue::UserData(u), d) => u.lua_to_dom_value(lua, Some(d)),
|
||||||
|
|
||||||
(v, d) => Err(DomConversionError::ToDomValue {
|
(v, d) => Err(DomConversionError::ToDomValue {
|
||||||
to: d.variant_name(),
|
to: d.variant_name().unwrap_or("???"),
|
||||||
from: v.type_name(),
|
from: v.type_name(),
|
||||||
detail: None,
|
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 {
|
macro_rules! userdata_to_dom {
|
||||||
($userdata:ident as $from_type:ty => $to_type:ty) => {
|
($userdata:ident as $from_type:ty => $to_type:ty) => {
|
||||||
match $userdata.borrow::<$from_type>() {
|
match $userdata.borrow::<$from_type>() {
|
||||||
|
@ -212,7 +222,7 @@ impl<'lua> DomValueToLua<'lua> for LuaAnyUserData<'lua> {
|
||||||
|
|
||||||
v => {
|
v => {
|
||||||
Err(DomConversionError::FromDomValue {
|
Err(DomConversionError::FromDomValue {
|
||||||
from: v.variant_name(),
|
from: v.variant_name().unwrap_or("???"),
|
||||||
to: "userdata",
|
to: "userdata",
|
||||||
detail: Some("Type not supported".to_string()),
|
detail: Some("Type not supported".to_string()),
|
||||||
})
|
})
|
||||||
|
@ -282,7 +292,7 @@ impl<'lua> LuaToDomValue<'lua> for LuaAnyUserData<'lua> {
|
||||||
|
|
||||||
ty => {
|
ty => {
|
||||||
return Err(DomConversionError::ToDomValue {
|
return Err(DomConversionError::ToDomValue {
|
||||||
to: ty.variant_name(),
|
to: ty.variant_name().unwrap_or("???"),
|
||||||
from: "userdata",
|
from: "userdata",
|
||||||
detail: Some("Type not supported".to_string()),
|
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::<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::<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::<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::<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::<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::<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::<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::<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::<Region3int16>() => userdata_to_dom!(value as Region3int16 => dom::Region3int16),
|
||||||
value if value.is::<UDim>() => userdata_to_dom!(value as UDim => dom::UDim),
|
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::*;
|
use super::*;
|
||||||
|
|
||||||
pub(super) trait DomValueExt {
|
pub(super) trait DomValueExt {
|
||||||
fn variant_name(&self) -> &'static str;
|
fn variant_name(&self) -> Option<&'static str>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DomValueExt for DomType {
|
impl DomValueExt for DomType {
|
||||||
fn variant_name(&self) -> &'static str {
|
fn variant_name(&self) -> Option<&'static str> {
|
||||||
use DomType::*;
|
use DomType::*;
|
||||||
match self {
|
Some(match self {
|
||||||
Axes => "Axes",
|
Axes => "Axes",
|
||||||
BinaryString => "BinaryString",
|
BinaryString => "BinaryString",
|
||||||
Bool => "Bool",
|
Bool => "Bool",
|
||||||
|
@ -40,13 +44,54 @@ impl DomValueExt for DomType {
|
||||||
Vector3 => "Vector3",
|
Vector3 => "Vector3",
|
||||||
Vector3int16 => "Vector3int16",
|
Vector3int16 => "Vector3int16",
|
||||||
OptionalCFrame => "OptionalCFrame",
|
OptionalCFrame => "OptionalCFrame",
|
||||||
_ => "?",
|
_ => return None,
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DomValueExt for DomValue {
|
impl DomValueExt for DomValue {
|
||||||
fn variant_name(&self) -> &'static str {
|
fn variant_name(&self) -> Option<&'static str> {
|
||||||
self.ty().variant_name()
|
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()?,
|
.build_readonly()?,
|
||||||
)?
|
)?
|
||||||
.into_function()?;
|
.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
|
// Functions in the built-in coroutine library also need to be
|
||||||
// replaced, these are a bit different than the ones above because
|
// replaced, these are a bit different than the ones above because
|
||||||
// calling resume or the function that wrap returns must return
|
// calling resume or the function that wrap returns must return
|
||||||
// whatever lua value(s) that the thread or task yielded back
|
// whatever lua value(s) that the thread or task yielded back
|
||||||
|
let globals = lua.globals();
|
||||||
let coroutine = globals.get::<_, LuaTable>("coroutine")?;
|
let coroutine = globals.get::<_, LuaTable>("coroutine")?;
|
||||||
coroutine.set("status", lua.create_function(coroutine_status)?)?;
|
coroutine.set("status", lua.create_function(coroutine_status)?)?;
|
||||||
coroutine.set("resume", lua.create_function(coroutine_resume)?)?;
|
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)
|
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
|
Coroutine library overrides for compat with task scheduler
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1,6 +1,12 @@
|
||||||
use mlua::prelude::*;
|
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
|
// HACK: We need to preserve the default behavior of the
|
||||||
// print and error functions, for pcall and such, which
|
// 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(())
|
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
|
// 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)?),
|
("print", lua.create_function(top_level::print)?),
|
||||||
("warn", lua.create_function(top_level::warn)?),
|
("warn", lua.create_function(top_level::warn)?),
|
||||||
("error", lua.create_function(top_level::error)?),
|
("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
|
// Set top-level globals
|
||||||
|
|
|
@ -147,4 +147,6 @@ create_tests! {
|
||||||
roblox_instance_methods_is_a: "roblox/instance/methods/IsA",
|
roblox_instance_methods_is_a: "roblox/instance/methods/IsA",
|
||||||
roblox_instance_methods_is_ancestor_of: "roblox/instance/methods/IsAncestorOf",
|
roblox_instance_methods_is_ancestor_of: "roblox/instance/methods/IsAncestorOf",
|
||||||
roblox_instance_methods_is_descendant_of: "roblox/instance/methods/IsDescendantOf",
|
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