--[=[
	Binds an update event to a signal until the update function stops returning a truthy
	value.

	@param signal Signal | RBXScriptSignal
	@param update () -> boolean -- should return true while it needs to update
	@return (...) -> () -- Connect function
	@return () -> () -- Disconnect function
]=]

function StepUtils.bindToSignal(signal, update)
	if typeof(signal) ~= "RBXScriptSignal" then
		error("signal must be of type RBXScriptSignal")
	end
	if type(update) ~= "function" then
		error(("update must be of type function, got %q"):format(type(update)))
	end

	local conn = nil
	local function disconnect()
		if conn then
			conn:Disconnect()
			conn = nil
		end
	end

	local function connect(...)
		-- Ignore if we have an existing connection
		if conn and conn.Connected then
			return
		end

		-- Check to see if we even need to bind an update
		if not update(...) then
			return
		end

		-- Avoid reentrance, if update() triggers another connection, we'll already be connected.
		if conn and conn.Connected then
			return
		end

		-- Usually contains just the self arg!
		local args = {...}

		-- Bind to render stepped
		conn = signal:Connect(function()
			if not update(unpack(args)) then
				disconnect()
			end
		end)
	end

	return connect, disconnect
end
