Software / code / prosody
File
util/fsm.lua @ 13317:e6a5f196fc1f
util.uuid: Add UUIDv7
Allows sorting by id as a substitute for sorting by timestamp since it
has the timestamp in the encoded in the first part, and only things that
happen extremely close together may get out of order by such a sort,
which might not matter.
From draft-ietf-uuidrev-rfc4122bis formerly draft-peabody-dispatch-new-uuid-format
| author | Kim Alvefur <zash@zash.se> |
|---|---|
| date | Sun, 15 Aug 2021 14:44:21 +0200 |
| parent | 13165:9c13c11b199d |
line wrap: on
line source
local events = require "prosody.util.events"; local fsm_methods = {}; local fsm_mt = { __index = fsm_methods }; local function is_fsm(o) local mt = getmetatable(o); return mt == fsm_mt; end local function notify_transition(fire_event, transition_event) local ret; ret = fire_event("transition", transition_event); if ret ~= nil then return ret; end if transition_event.from ~= transition_event.to then ret = fire_event("leave/"..transition_event.from, transition_event); if ret ~= nil then return ret; end end ret = fire_event("transition/"..transition_event.name, transition_event); if ret ~= nil then return ret; end end local function notify_transitioned(fire_event, transition_event) if transition_event.to ~= transition_event.from then fire_event("enter/"..transition_event.to, transition_event); end if transition_event.name then fire_event("transitioned/"..transition_event.name, transition_event); end fire_event("transitioned", transition_event); end local function do_transition(name) return function (self, attr) local new_state = self.fsm.states[self.state][name] or self.fsm.states["*"][name]; if not new_state then return error(("Invalid state transition: %s cannot %s"):format(self.state, name)); end local transition_event = { instance = self; name = name; to = new_state; to_attr = attr; from = self.state; from_attr = self.state_attr; }; local fire_event = self.fsm.events.fire_event; local ret = notify_transition(fire_event, transition_event); if ret ~= nil then return nil, ret; end self.state = new_state; self.state_attr = attr; notify_transitioned(fire_event, transition_event); return true; end; end local function new(desc) local self = setmetatable({ default_state = desc.default_state; events = events.new(); }, fsm_mt); -- states[state_name][transition_name] = new_state_name local states = { ["*"] = {} }; if desc.default_state then states[desc.default_state] = {}; end self.states = states; local instance_methods = {}; self._instance_mt = { __index = instance_methods }; for _, transition in ipairs(desc.transitions or {}) do local from_states = transition.from; if type(from_states) ~= "table" then from_states = { from_states }; end for _, from in ipairs(from_states) do if not states[from] then states[from] = {}; end if not states[transition.to] then states[transition.to] = {}; end if states[from][transition.name] then return error(("Duplicate transition in FSM specification: %s from %s"):format(transition.name, from)); end states[from][transition.name] = transition.to; end -- Add public method to trigger this transition instance_methods[transition.name] = do_transition(transition.name); end if desc.state_handlers then for state_name, handler in pairs(desc.state_handlers) do self.events.add_handler("enter/"..state_name, handler); end end if desc.transition_handlers then for transition_name, handler in pairs(desc.transition_handlers) do self.events.add_handler("transition/"..transition_name, handler); end end if desc.handlers then self.events.add_handlers(desc.handlers); end return self; end function fsm_methods:init(state_name, state_attr) local initial_state = assert(state_name or self.default_state, "no initial state specified"); if not self.states[initial_state] then return error("Invalid initial state: "..initial_state); end local instance = setmetatable({ fsm = self; state = initial_state; state_attr = state_attr; }, self._instance_mt); if initial_state ~= self.default_state then local fire_event = self.events.fire_event; notify_transitioned(fire_event, { instance = instance; to = initial_state; to_attr = state_attr; from = self.default_state; }); end return instance; end function fsm_methods:is_instance(o) local mt = getmetatable(o); return mt == self._instance_mt; end return { new = new; is_fsm = is_fsm; };