Skip to content

Instantly share code, notes, and snippets.

@SkyyySi
Created June 11, 2025 08:28
Show Gist options
  • Save SkyyySi/90c90512952fdeafdf6292b9461f6097 to your computer and use it in GitHub Desktop.
Save SkyyySi/90c90512952fdeafdf6292b9461f6097 to your computer and use it in GitHub Desktop.
local assert = assert
local error = error
local ipairs = ipairs
local pairs = pairs
local print = print
local setmetatable = setmetatable
local tostring = tostring
local type = type
local format = string.format
--------------------------------------------------------------------------------
---@param func_name string
---@param index integer
---@param value any
---@param expected_type type
---@return true | false
---@return nil | string
---@nodiscard
local function check_param_helper(func_name, index, value, expected_type)
local ty = type(value)
if ty == expected_type then
return true, nil
end
return false, format(
"bad argument #%d to '%s' (%s expected, got %s)",
index,
func_name,
expected_type,
ty
)
end
---@param func_name string
---@param params [any, type][]
---@return true | false
---@return nil | string
---@nodiscard
local function check_params(func_name, params)
for index, param in ipairs(params) do
local value = param[1]
local expected_type = param[2]
local is_ok, error_message = check_param_helper(func_name, index, value, expected_type)
if not is_ok then
return is_ok, error_message
end
end
return true, nil
end
--------------------------------------------------------------------------------
---@alias SignalCallback fun(
--- self: Signal,
--- ...: unknown,
--- )
---@class Signal : Signal_base
---@field name string
---@field callbacks { [SignalCallback]?: true | nil }
---@class Signal_base
local Signal_base = {}
Signal_base.__index = Signal_base
---@param self Signal
---@param fn SignalCallback
function Signal_base:connect(fn)
assert(check_params("connect", {
{ self, "table" },
{ fn, "function" },
}))
assert(self.callbacks[fn] == nil)
self.callbacks[fn] = true
end
---@param self Signal
---@param fn SignalCallback
function Signal_base:disconnect(fn)
assert(check_params("disconnect", {
{ self, "table" },
{ fn, "function" },
}))
assert(self.callbacks[fn] ~= nil)
self.callbacks[fn] = nil
end
---@param self Signal
---@param ... unknown
function Signal_base:send(...)
for fn, _ in pairs(self.callbacks) do
fn(self, ...)
end
end
---@class Signal_class : Signal_base
---@overload fun(
--- name: string,
--- callbacks?: { [SignalCallback]?: true | nil },
--- ): self: Signal
local Signal = setmetatable({
---@protected
__name = "Signal",
---@protected
__base = Signal_base,
---@protected
__init = function(self, name, callbacks)
if callbacks == nil then
callbacks = {}
end
assert(check_params("__init", {
{ self, "table" },
{ name, "string" },
{ callbacks, "table" },
}))
self.name = name
self.callbacks = callbacks
end,
}, {
__index = Signal_base,
---@param cls Signal_class
---@return Signal
__call = function(cls, ...)
local self = setmetatable({}, cls.__base) --[[@as Signal]]
cls.__init(self, ...)
return self
end,
} --[[@as any]])
Signal_base.__class = Signal
--------------------------------------------------------------------------------
do
local my_signal = Signal("MyApp::Example")
my_signal:connect(function(self, value)
print(format(
"Signal %q was emitted with value: %s = %s",
self.name,
type(value),
tostring(value)
))
end)
my_signal:send(123456)
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment