mirror of
https://github.com/CompeyDev/rusty-luau.git
synced 2025-04-07 19:30:54 +01:00
feat: roblox event polyfills
* RobloxEvent as a wrapper around BindableEvents providing the same API type as LuauSignal * Future.poll now returns a Result instead of a force unwrap
This commit is contained in:
parent
c42e4ad3fe
commit
edcef2015f
9 changed files with 206 additions and 87 deletions
|
@ -5,7 +5,7 @@
|
||||||
current: "path",
|
current: "path",
|
||||||
target: {
|
target: {
|
||||||
name: "roblox",
|
name: "roblox",
|
||||||
indexing_style: "wait_for_child",
|
indexing_style: "property",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -3,3 +3,6 @@ Packages/
|
||||||
|
|
||||||
# Moonwave compiled docs
|
# Moonwave compiled docs
|
||||||
build
|
build
|
||||||
|
|
||||||
|
# Bundled roblox files
|
||||||
|
rbx
|
|
@ -1,17 +1,11 @@
|
||||||
local isRoblox = _VERSION == "Luau"
|
local isRoblox = _VERSION == "Luau"
|
||||||
local isLune = _VERSION:find("Lune") == 1
|
local isLune = _VERSION:find("Lune") == 1
|
||||||
local mod = if isLune then require("../deps") else require(script.Parent.deps)
|
local deps = if isLune then require("../deps") else require(script.Parent.deps)
|
||||||
local requires = mod(isRoblox, isLune)
|
local requires = deps(isRoblox, isLune)
|
||||||
|
local util = require("./util.luau")
|
||||||
|
|
||||||
local Signal: {
|
local Signal = util.Signal
|
||||||
new: <T...>() -> Signal<T...>,
|
type Signal<T...> = util.Signal<T...>
|
||||||
} = requires.signal
|
|
||||||
type Signal<T...> = {
|
|
||||||
Fire: (self: Signal<T...>, T...) -> (),
|
|
||||||
Connect: (self: Signal<T...>, callback: (T...) -> ()) -> () -> (),
|
|
||||||
Once: (self: Signal<T...>, callback: (T...) -> ()) -> () -> (),
|
|
||||||
DisconnectAll: (self: Signal<T...>) -> (),
|
|
||||||
}
|
|
||||||
|
|
||||||
local task = requires.task
|
local task = requires.task
|
||||||
|
|
||||||
|
@ -210,13 +204,13 @@ end
|
||||||
@param self Future<T>
|
@param self Future<T>
|
||||||
@return T -- The value returned by the function on completion
|
@return T -- The value returned by the function on completion
|
||||||
]=]
|
]=]
|
||||||
function Future.await<T>(self: Future<T>): T
|
function Future.await<T>(self: Future<T>): Option<T>
|
||||||
while true do
|
while true do
|
||||||
local status: Status, ret: Option<T> = self:poll()
|
local status: Status, ret: Option<T> = self:poll()
|
||||||
|
|
||||||
if status == "ready" then
|
if status == "ready" then
|
||||||
-- Safe to unwrap, we know it must not be nil
|
-- Safe to unwrap, we know it must not be nil
|
||||||
return ret:unwrap()
|
return ret
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
118
lib/util.luau
118
lib/util.luau
|
@ -1,38 +1,102 @@
|
||||||
|
local isRoblox = _VERSION == "Luau"
|
||||||
|
local isLune = _VERSION:find("Lune") == 1
|
||||||
|
local deps = if isLune then require("../deps") elseif isRoblox then require(script.Parent.deps) else error("Unsupported Runtime!")
|
||||||
|
local requires = deps(isRoblox, isLune)
|
||||||
|
|
||||||
|
local LuauSignal: {
|
||||||
|
new: <T...>() -> Signal<T...>,
|
||||||
|
} = requires.signal
|
||||||
|
|
||||||
|
export type Signal<T...> = {
|
||||||
|
Fire: (self: Signal<T...>, T...) -> (),
|
||||||
|
Connect: (self: Signal<T...>, callback: (T...) -> ()) -> () -> (),
|
||||||
|
Once: (self: Signal<T...>, callback: (T...) -> ()) -> () -> (),
|
||||||
|
DisconnectAll: (self: Signal<T...>) -> (),
|
||||||
|
}
|
||||||
|
|
||||||
-- From https://gist.github.com/sapphyrus/fd9aeb871e3ce966cc4b0b969f62f539
|
-- From https://gist.github.com/sapphyrus/fd9aeb871e3ce966cc4b0b969f62f539
|
||||||
local function tableEq(tbl1, tbl2)
|
local function tableEq(tbl1, tbl2)
|
||||||
if tbl1 == tbl2 then
|
-- if tbl1 == tbl2 then
|
||||||
return true
|
-- return true
|
||||||
elseif type(tbl1) == "table" and type(tbl2) == "table" then
|
-- elseif type(tbl1) == "table" and type(tbl2) == "table" then
|
||||||
for key1, value1 in pairs(tbl1) do
|
-- for key1, value1 in pairs(tbl1) do
|
||||||
local value2 = tbl2[key1]
|
-- local value2 = tbl2[key1]
|
||||||
|
|
||||||
if value2 == nil then
|
-- if value2 == nil then
|
||||||
-- avoid the type call for missing keys in tbl2 by directly comparing with nil
|
-- -- avoid the type call for missing keys in tbl2 by directly comparing with nil
|
||||||
return false
|
-- return false
|
||||||
elseif value1 ~= value2 then
|
-- elseif value1 ~= value2 then
|
||||||
if type(value1) == "table" and type(value2) == "table" then
|
-- if type(value1) == "table" and type(value2) == "table" then
|
||||||
if not tableEq(value1, value2) then
|
-- if not tableEq(value1, value2) then
|
||||||
return false
|
-- return false
|
||||||
end
|
-- end
|
||||||
else
|
-- else
|
||||||
return false
|
-- return false
|
||||||
end
|
-- end
|
||||||
end
|
-- end
|
||||||
|
-- end
|
||||||
|
|
||||||
|
-- -- check for missing keys in tbl1
|
||||||
|
-- for key2, _ in pairs(tbl2) do
|
||||||
|
-- if tbl1[key2] == nil then
|
||||||
|
-- return false
|
||||||
|
-- end
|
||||||
|
-- end
|
||||||
|
|
||||||
|
-- return true
|
||||||
|
-- end
|
||||||
|
|
||||||
|
-- return false
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
local RobloxEvent = {}
|
||||||
|
type RobloxEvent<T...> = Signal<T...> & {
|
||||||
|
_inner: BindableEvent
|
||||||
|
}
|
||||||
|
|
||||||
|
function RobloxEvent.new<T...>()
|
||||||
|
local instance = Instance.new("BindableEvent")
|
||||||
|
instance.Parent = script
|
||||||
|
instance.Name = tostring({}):split(" ")[2]
|
||||||
|
|
||||||
|
return setmetatable({
|
||||||
|
_inner = instance
|
||||||
|
}, {
|
||||||
|
__index = RobloxEvent
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
function RobloxEvent.Fire<T...>(self: RobloxEvent<T...>, ...: T...)
|
||||||
|
return self._inner:Fire(...)
|
||||||
|
end
|
||||||
|
|
||||||
|
function RobloxEvent.Connect<T...>(self: RobloxEvent<T...>, callback: (T...) -> ())
|
||||||
|
local conn = self._inner.Event:Connect(callback)
|
||||||
|
|
||||||
|
return {
|
||||||
|
DisconnectAll = function<T...>(self: RobloxEvent<T...>)
|
||||||
|
conn:Disconnect()
|
||||||
|
self._inner:Destroy()
|
||||||
end
|
end
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
-- check for missing keys in tbl1
|
function RobloxEvent.Once<T...>(self: RobloxEvent<T...>, callback: (T...) -> ())
|
||||||
for key2, _ in pairs(tbl2) do
|
local conn = self._inner.Event:Connect(callback)
|
||||||
if tbl1[key2] == nil then
|
|
||||||
return false
|
return {
|
||||||
end
|
DisconnectAll = function<T...>(self: RobloxEvent<T...>)
|
||||||
|
conn:Disconnect()
|
||||||
|
self._inner:Destroy()
|
||||||
end
|
end
|
||||||
|
}
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
return false
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return {
|
return {
|
||||||
tableEq = tableEq,
|
tableEq = tableEq,
|
||||||
|
Signal = setmetatable({}, {
|
||||||
|
__index = if isLune then LuauSignal elseif isRoblox then RobloxEvent else error("Unsupported runtime!"),
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,13 +7,13 @@
|
||||||
would rather import this conversion module.
|
would rather import this conversion module.
|
||||||
]]
|
]]
|
||||||
|
|
||||||
local option = require(script.Parent:WaitForChild('option'))
|
local option = require(script.Parent.option)
|
||||||
local Option = option.Option
|
local Option = option.Option
|
||||||
export type Option<T> = option.Option<T>
|
export type Option<T> = option.Option<T>
|
||||||
local None = option.None
|
local None = option.None
|
||||||
local Some = option.Some
|
local Some = option.Some
|
||||||
|
|
||||||
local result = require(script.Parent:WaitForChild('result'))
|
local result = require(script.Parent.result)
|
||||||
local Result = result.Result
|
local Result = result.Result
|
||||||
export type Result<T, E> = result.Result<T, E>
|
export type Result<T, E> = result.Result<T, E>
|
||||||
local Ok = result.Ok
|
local Ok = result.Ok
|
||||||
|
|
|
@ -1,26 +1,20 @@
|
||||||
local isRoblox = _VERSION == "Luau"
|
local isRoblox = _VERSION == "Luau"
|
||||||
local isLune = _VERSION:find("Lune") == 1
|
local isLune = _VERSION:find("Lune") == 1
|
||||||
local mod = if isLune then require(script.Parent.Parent:WaitForChild('deps'))else require(script.Parent.deps)
|
local deps = if isLune then require(script.Parent.Parent.deps)else require(script.Parent.deps)
|
||||||
local requires = mod(isRoblox, isLune)
|
local requires = deps(isRoblox, isLune)
|
||||||
|
local util = require(script.Parent.util)
|
||||||
|
|
||||||
local Signal: {
|
local Signal = util.Signal
|
||||||
new: <T...>() -> Signal<T...>
|
type Signal<T...> = util.Signal<T...>
|
||||||
} = requires.signal
|
|
||||||
type Signal<T...> = {
|
|
||||||
Fire: (self: Signal<T...>,T...) -> (),
|
|
||||||
Connect: (self: Signal<T...>, callback: (T...) -> ()) -> () -> (),
|
|
||||||
Once: (self: Signal<T...>, callback: (T...) -> ()) -> () -> (),
|
|
||||||
DisconnectAll: (self: Signal<T...>) -> ()
|
|
||||||
}
|
|
||||||
|
|
||||||
local task = requires.task
|
local task = requires.task
|
||||||
|
|
||||||
local result = require(script.Parent:WaitForChild('result'))
|
local result = require(script.Parent.result)
|
||||||
type Result<T, E> = result.Result<T, E>
|
type Result<T, E> = result.Result<T, E>
|
||||||
local Ok = result.Ok
|
local Ok = result.Ok
|
||||||
local Err = result.Err
|
local Err = result.Err
|
||||||
|
|
||||||
local option = require(script.Parent:WaitForChild('option'))
|
local option = require(script.Parent.option)
|
||||||
type Option<T> = option.Option<T>
|
type Option<T> = option.Option<T>
|
||||||
local None = option.None
|
local None = option.None
|
||||||
local Some = option.Some
|
local Some = option.Some
|
||||||
|
@ -210,13 +204,13 @@ end
|
||||||
@param self Future<T>
|
@param self Future<T>
|
||||||
@return T -- The value returned by the function on completion
|
@return T -- The value returned by the function on completion
|
||||||
]=]
|
]=]
|
||||||
function Future.await<T>(self: Future<T>): T
|
function Future.await<T>(self: Future<T>): Option<T>
|
||||||
while true do
|
while true do
|
||||||
local status: Status, ret: Option<T> = self:poll()
|
local status: Status, ret: Option<T> = self:poll()
|
||||||
|
|
||||||
if status == "ready" then
|
if status == "ready" then
|
||||||
-- Safe to unwrap, we know it must not be nil
|
-- Safe to unwrap, we know it must not be nil
|
||||||
return ret:unwrap()
|
return ret
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
local tableEq = require(script.Parent:WaitForChild('util')).tableEq
|
local tableEq = require(script.Parent.util).tableEq
|
||||||
|
|
||||||
--[=[
|
--[=[
|
||||||
@class Option
|
@class Option
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
local tableEq = require(script.Parent:WaitForChild('util')).tableEq
|
local tableEq = require(script.Parent.util).tableEq
|
||||||
|
|
||||||
--[=[
|
--[=[
|
||||||
@class Result
|
@class Result
|
||||||
|
|
118
rbx/util.luau
118
rbx/util.luau
|
@ -1,38 +1,102 @@
|
||||||
|
local isRoblox = _VERSION == "Luau"
|
||||||
|
local isLune = _VERSION:find("Lune") == 1
|
||||||
|
local deps = if isLune then require(script.Parent.Parent.deps)elseif isRoblox then require(script.Parent.deps) else error("Unsupported Runtime!")
|
||||||
|
local requires = deps(isRoblox, isLune)
|
||||||
|
|
||||||
|
local LuauSignal: {
|
||||||
|
new: <T...>() -> Signal<T...>
|
||||||
|
} = requires.signal
|
||||||
|
|
||||||
|
export type Signal<T...> = {
|
||||||
|
Fire: (self: Signal<T...>,T...) -> (),
|
||||||
|
Connect: (self: Signal<T...>, callback: (T...) -> ()) -> () -> (),
|
||||||
|
Once: (self: Signal<T...>, callback: (T...) -> ()) -> () -> (),
|
||||||
|
DisconnectAll: (self: Signal<T...>) -> ()
|
||||||
|
}
|
||||||
|
|
||||||
-- From https://gist.github.com/sapphyrus/fd9aeb871e3ce966cc4b0b969f62f539
|
-- From https://gist.github.com/sapphyrus/fd9aeb871e3ce966cc4b0b969f62f539
|
||||||
local function tableEq(tbl1, tbl2)
|
local function tableEq(tbl1, tbl2)
|
||||||
if tbl1 == tbl2 then
|
-- if tbl1 == tbl2 then
|
||||||
return true
|
-- return true
|
||||||
elseif type(tbl1) == "table" and type(tbl2) == "table" then
|
-- elseif type(tbl1) == "table" and type(tbl2) == "table" then
|
||||||
for key1, value1 in pairs(tbl1) do
|
-- for key1, value1 in pairs(tbl1) do
|
||||||
local value2 = tbl2[key1]
|
-- local value2 = tbl2[key1]
|
||||||
|
|
||||||
if value2 == nil then
|
-- if value2 == nil then
|
||||||
-- avoid the type call for missing keys in tbl2 by directly comparing with nil
|
-- -- avoid the type call for missing keys in tbl2 by directly comparing with nil
|
||||||
return false
|
-- return false
|
||||||
elseif value1 ~= value2 then
|
-- elseif value1 ~= value2 then
|
||||||
if type(value1) == "table" and type(value2) == "table" then
|
-- if type(value1) == "table" and type(value2) == "table" then
|
||||||
if not tableEq(value1, value2) then
|
-- if not tableEq(value1, value2) then
|
||||||
return false
|
-- return false
|
||||||
end
|
-- end
|
||||||
else
|
-- else
|
||||||
return false
|
-- return false
|
||||||
end
|
-- end
|
||||||
end
|
-- end
|
||||||
|
-- end
|
||||||
|
|
||||||
|
-- -- check for missing keys in tbl1
|
||||||
|
-- for key2, _ in pairs(tbl2) do
|
||||||
|
-- if tbl1[key2] == nil then
|
||||||
|
-- return false
|
||||||
|
-- end
|
||||||
|
-- end
|
||||||
|
|
||||||
|
-- return true
|
||||||
|
-- end
|
||||||
|
|
||||||
|
-- return false
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
local RobloxEvent = {}
|
||||||
|
type RobloxEvent<T...> = Signal<T...> & {
|
||||||
|
_inner: BindableEvent
|
||||||
|
}
|
||||||
|
|
||||||
|
function RobloxEvent.new<T...>()
|
||||||
|
local instance = Instance.new("BindableEvent")
|
||||||
|
instance.Parent = script
|
||||||
|
instance.Name = tostring({}):split(" ")[2]
|
||||||
|
|
||||||
|
return setmetatable({
|
||||||
|
_inner = instance
|
||||||
|
}, {
|
||||||
|
__index = RobloxEvent
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
function RobloxEvent.Fire<T...>(self: RobloxEvent<T...>, ...: T...)
|
||||||
|
return self._inner:Fire(...)
|
||||||
|
end
|
||||||
|
|
||||||
|
function RobloxEvent.Connect<T...>(self: RobloxEvent<T...>, callback: (T...) -> ())
|
||||||
|
local conn = self._inner.Event:Connect(callback)
|
||||||
|
|
||||||
|
return {
|
||||||
|
DisconnectAll = function<T...>(self: RobloxEvent<T...>)
|
||||||
|
conn:Disconnect()
|
||||||
|
self._inner:Destroy()
|
||||||
end
|
end
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
-- check for missing keys in tbl1
|
function RobloxEvent.Once<T...>(self: RobloxEvent<T...>, callback: (T...) -> ())
|
||||||
for key2, _ in pairs(tbl2) do
|
local conn = self._inner.Event:Connect(callback)
|
||||||
if tbl1[key2] == nil then
|
|
||||||
return false
|
return {
|
||||||
end
|
DisconnectAll = function<T...>(self: RobloxEvent<T...>)
|
||||||
|
conn:Disconnect()
|
||||||
|
self._inner:Destroy()
|
||||||
end
|
end
|
||||||
|
}
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
return false
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return {
|
return {
|
||||||
tableEq = tableEq,
|
tableEq = tableEq,
|
||||||
|
Signal = setmetatable({}, {
|
||||||
|
__index = if isLune then LuauSignal elseif isRoblox then RobloxEvent else error("Unsupported runtime!"),
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue