module("luci.lpk.core", package.seeall) require("luci.util") Task = luci.util.class() function Task.__init__(self, machine, register, start) self.machine = machine -- The queue that has to be processed self.work = {start} -- The queue that has to be processed in case of rollback self.done = {} -- The Task register self.register = register end function Task.rollback(self) if #self.done < 1 then return false end local state = table.remove(self.done) local ret, err = pcall(state.rollback, state, self.register) if ret then return true else return false, err end end function Task.step(self) local state = table.remove(self.work) local ret, next = pcall(state.process, state, self.register) if ret then if next then local nstate = self.machine:state(next) if nstate then table.insert(self.work, state) table.insert(self.work, nstate) else self.register.error = "Unknown state: " .. next return false end else table.insert(self.done, state) end return #self.work > 0 else self.register.error = next return false end end function Task.perform(self) while self:step() do end if not self.register.error then return true else local stat, err repeat stat, err = self:rollback() until not stat assert(not err, "Machine broken!") return false, self.register.error end end Machine = luci.util.class() function Machine.__init__(self, namespace) self.namespace = namespace or _NAME end function Machine.state(self, name) local ret, state = pcall(require, self.namespace .. "." .. name) return ret and state end function Machine.task(self, name, ...) local start = self:state(name) if not start or not start.entry then error("No such command: " .. name) end local register = {} if start:entry(register) then return Task(self, register, start) else return nil, register.error end end