summaryrefslogtreecommitdiff
path: root/lua/clever_f/composition_root.lua
blob: 527fec61b3d3f8bdbf8a7e182c1612b5fcd439b4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
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,
    last_highlight_refresh = 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
    local highlights = record.feedback:evaluate_highlights()
    local colorscheme_registration = record.host:register_events(
      "ColorScheme",
      function()
        record.last_highlight_refresh = record.feedback:evaluate_highlights()
      end,
      { owner = "clever_f", lifecycle = "colorscheme" }
    )
    record.activation = {
      state = record.state,
      highlights = highlights,
      colorscheme_registration = colorscheme_registration,
    }
  end
  return record.activation
end

function CompositionRoot:last_highlight_refresh()
  return root_records[self].last_highlight_refresh
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