local domain = require("clever_f.domain") local sequence_coordinator = require("clever_f.sequence_coordinator") local M = {} local ActionFacade = {} ActionFacade.__index = ActionFacade M.ActionFacade = ActionFacade local facade_records = setmetatable({}, { __mode = "k" }) local function fail(message, level) error(message, (level or 1) + 1) end function ActionFacade.new(options) if ActionFacade.is(options) then return options end local coordinator if sequence_coordinator.SequenceCoordinator.is(options) then coordinator = options elseif type(options) == "table" then coordinator = options.coordinator or options.sequence_coordinator or sequence_coordinator.new(options) else fail("ActionFacade options must be a table", 2) end if not sequence_coordinator.SequenceCoordinator.is(coordinator) then fail("ActionFacade requires a SequenceCoordinator", 2) end local facade = setmetatable({}, ActionFacade) facade_records[facade] = { coordinator = coordinator } return facade end function ActionFacade.is(value) return type(value) == "table" and facade_records[value] ~= nil end function ActionFacade:coordinator() return facade_records[self].coordinator end function ActionFacade:primary(descriptor) local outcome = self:coordinator():primary(descriptor) if not domain.ActionOutcome.is(outcome) then fail("SequenceCoordinator must return an ActionOutcome", 2) end return outcome end ActionFacade.invoke_primary = ActionFacade.primary ActionFacade.start = ActionFacade.primary local function explicit_outcome(facade, method_name) local coordinator = facade:coordinator() local outcome = coordinator[method_name](coordinator) if not domain.ActionOutcome.is(outcome) then fail("SequenceCoordinator must return an ActionOutcome", 3) end return outcome end function ActionFacade:repeat_same_direction() return explicit_outcome(self, "repeat_same_direction") end function ActionFacade:repeat_opposite_direction() return explicit_outcome(self, "repeat_opposite_direction") end ActionFacade.RepeatSameDirection = ActionFacade.repeat_same_direction ActionFacade.RepeatOppositeDirection = ActionFacade.repeat_opposite_direction function M.new(options) return ActionFacade.new(options) end setmetatable(M, { __call = function(_, options) return ActionFacade.new(options) end, }) return M