local M = {} function M.dump(o) if type(o) == 'table' then local s = '{' for k, v in pairs(o) do if type(k) ~= 'number' then k = '"' .. k .. '"' end s = s .. ' [' .. k .. '] = ' .. M.dump(v) .. ',' end return s .. '} ' else return tostring(o) end end -- Coroutine-based async/await helpers function M.await(callback_fn) local co = coroutine.running() if not co then error 'await must be called inside async' end local result, err local done = false callback_fn(function(res, e) result = res err = e done = true if coroutine.status(co) == 'suspended' then coroutine.resume(co) end end) if not done then coroutine.yield() end if err then error(err) end return result end function M.awaitAll(callback_fns) local co = coroutine.running() if not co then error 'awaitAll must be called inside async' end local results = {} local pending = #callback_fns local err = nil for i, fn in ipairs(callback_fns) do fn(function(res, e) if e then err = e end results[i] = res pending = pending - 1 if pending == 0 and coroutine.status(co) == 'suspended' then coroutine.resume(co) end end) end if pending > 0 then coroutine.yield() end if err then error(err) end return results end function M.async(fn) return function(...) local args = { ... } local co = coroutine.create(function() fn(table.unpack(args)) end) local ok, err = coroutine.resume(co) if not ok then print('Async error: ' .. tostring(err)) end end end -- Execute a shell command, returns a callback function for use with await function M.exec(cmd) return function(resolve) sbar.exec(cmd, function(result, exit_code) if exit_code ~= 0 then resolve(nil, string.format('Exit Code: %s Message: %s', tostring(exit_code), M.dump(result))) else resolve(result, nil) end end) end end return M