local action_facade = require("clever_f.action_facade") local capabilities = require("clever_f.capabilities") local feedback_service = require("clever_f.feedback_service") local policy = require("clever_f.policy") local sequence_coordinator = require("clever_f.sequence_coordinator") local sequence_state = require("clever_f.sequence_state") local state_transitions = require("clever_f.state_transitions") local M = {} local CompositionRoot = {} CompositionRoot.__index = CompositionRoot M.CompositionRoot = CompositionRoot local root_records = setmetatable({}, { __mode = "k" }) local function fail(message, level) error(message, (level or 1) + 1) end local function normalize_options(options) if type(options) ~= "table" then fail("CompositionRoot options must be a table", 3) end if options.host == nil then return { host = options } end return options end function CompositionRoot.new(options) if CompositionRoot.is(options) then return options end options = normalize_options(options) local host = capabilities.assert_implements(options.host) local state = sequence_state.new() local transitions = options.transitions or options.state_transitions or state_transitions.new(state) local policy_service = options.policy or options.policy_service or policy.new(host) local feedback = options.feedback or options.feedback_service or feedback_service.new({ host = host, state = state, transitions = transitions, policy = policy_service, }) local coordinator = options.coordinator or options.sequence_coordinator or sequence_coordinator.new({ host = host, state = state, transitions = transitions, policy = policy_service, feedback = feedback, }) local facade = options.facade or options.action_facade or action_facade.new({ coordinator = coordinator }) local root = setmetatable({}, CompositionRoot) root_records[root] = { host = host, state = state, transitions = transitions, policy = policy_service, feedback = feedback, coordinator = coordinator, facade = facade, activation = nil, } return root end function CompositionRoot.is(value) return type(value) == "table" and root_records[value] ~= nil end function CompositionRoot:activate() local record = root_records[self] if record.activation == nil then record.activation = { state = record.state, highlights = record.feedback:evaluate_highlights(), } end return record.activation end function CompositionRoot:host() return root_records[self].host end function CompositionRoot:state() return root_records[self].state end function CompositionRoot:transitions() return root_records[self].transitions end function CompositionRoot:policy() return root_records[self].policy end function CompositionRoot:feedback() return root_records[self].feedback end function CompositionRoot:coordinator() return root_records[self].coordinator end function CompositionRoot:facade() return root_records[self].facade end function M.new(options) return CompositionRoot.new(options) end setmetatable(M, { __call = function(_, options) return CompositionRoot.new(options) end, }) return M