summaryrefslogtreecommitdiff
path: root/lua
diff options
context:
space:
mode:
authorJackson Moore <jacksonmoore@tuta.io>2026-09-04 14:16:18 +0200
committerJackson Moore <jacksonmoore@tuta.io>2026-09-04 14:16:18 +0200
commit401a2851c643c5051c75310704cfdc94d6b0a072 (patch)
treefa99960f4b41608f8cc5d0761f6453973fb1f759 /lua
parente192a496e9264249ff60b625453a3f1227af2221 (diff)
Compose global state activation
Diffstat (limited to 'lua')
-rw-r--r--lua/clever_f/composition_root.lua130
1 files changed, 130 insertions, 0 deletions
diff --git a/lua/clever_f/composition_root.lua b/lua/clever_f/composition_root.lua
new file mode 100644
index 0000000..e04c177
--- /dev/null
+++ b/lua/clever_f/composition_root.lua
@@ -0,0 +1,130 @@
+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,
+ }
+ 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