local M = {} -- adds tokens to the current wait list, does not change order/unordered M.wait = function(self, ...) local tlist = { ... } for _, token in ipairs(tlist) do if type(token) ~= 'string' then error('Wait tokens must be strings. Got '..type(token), 2) end table.insert(self.tokens, token) end end -- set list as unordered, adds tokens to current wait list M.wait_unordered = function(self, ...) self.ordered = false self:wait(...) end -- set list as ordered, adds tokens to current wait list M.wait_ordered = function(self, ...) self.ordered = true self:wait(...) end -- generates a message listing tokens received/open M.tokenlist = function(self) local list if #self.tokens_done == 0 then list = 'No tokens received.' else list = 'Tokens received ('..tostring(#self.tokens_done)..')' local s = ': ' for _,t in ipairs(self.tokens_done) do list = list .. s .. '\''..t..'\'' s = ', ' end list = list .. '.' end if #self.tokens == 0 then list = list .. ' No more tokens expected.' else list = list .. ' Tokens not received ('..tostring(#self.tokens)..')' local s = ': ' for _, t in ipairs(self.tokens) do list = list .. s .. '\''..t..'\'' s = ', ' end list = list .. '.' end return list end -- marks a token as completed, checks for ordered/unordered, checks for completeness M.done = function(self, ...) self:_done(...) end -- extra wrapper for same error level constant as __call method M._done = function(self, token) if token then if type(token) ~= 'string' then error('Wait tokens must be strings. Got '..type(token), 3) end if self.ordered then if self.tokens[1] == token then table.remove(self.tokens, 1) table.insert(self.tokens_done, token) else if self.tokens[1] then error(('Bad token, expected \'%s\' got \'%s\'. %s'):format(self.tokens[1], token, self:tokenlist()), 3) else error(('Bad token (no more tokens expected) got \'%s\'. %s'):format(token, self:tokenlist()), 3) end end else -- unordered for i, t in ipairs(self.tokens) do if t == token then table.remove(self.tokens, i) table.insert(self.tokens_done, token) token = nil break end end if token then error(('Unknown token \'%s\'. %s'):format(token, self:tokenlist()), 3) end end end if not next(self.tokens) then -- no more tokens, so we're really done... self.done_cb() end end -- wraps a done callback into a done-object supporting tokens to sign-off M.new = function(done_callback) local obj = { tokens = {}, tokens_done = {}, done_cb = done_callback, ordered = true, -- default for sign off of tokens } return setmetatable( obj, { __call = function(self, ...) self:_done(...) end, __index = M, }) end return M