summaryrefslogtreecommitdiff
path: root/script/core/collector.lua
blob: 62ca5a7dd64a992390978a7f4d0c6f0d5e100105 (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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
local scope = require 'workspace.scope'

---@class collector
---@field subscribed table<uri, table<string, any>>
---@field collect table<string, table<uri, any>>
local mt = {}
mt.__index = mt

--- 订阅一个名字
---@param uri uri
---@param name string
---@param value any
function mt:subscribe(uri, name, value)
    uri = uri or '<fallback>'
    -- 订阅部分
    local uriSubscribed = self.subscribed[uri]
    if not uriSubscribed then
        uriSubscribed = {}
        self.subscribed[uri] = uriSubscribed
    end
    uriSubscribed[name] = true
    -- 收集部分
    local nameCollect = self.collect[name]
    if not nameCollect then
        nameCollect = {}
        self.collect[name] = nameCollect
    end
    if value == nil then
        value = true
    end
    nameCollect[uri] = value
end

--- 丢弃掉某个 uri 中收集的所有信息
---@param uri uri
function mt:dropUri(uri)
    uri = uri or '<fallback>'
    local uriSubscribed = self.subscribed[uri]
    if not uriSubscribed then
        return
    end
    self.subscribed[uri] = nil
    for name in pairs(uriSubscribed) do
        self.collect[name][uri] = nil
    end
end

function mt:dropAll()
    self.subscribed = {}
    self.collect    = {}
end

--- 是否包含某个名字的订阅
---@param name string
---@return boolean
function mt:has(name)
    local nameCollect = self.collect[name]
    if not nameCollect then
        return false
    end
    if next(nameCollect) == nil then
        self.collect[name] = nil
        return false
    end
    return true
end

local DUMMY_FUNCTION = function () end

---@param scp scope
local function eachOfFolder(nameCollect, scp)
    local curi, value

    local function getNext()
        curi, value = next(nameCollect, curi)
        if not curi then
            return nil, nil
        end
        if scp:isChildUri(curi)
        or scp:isLinkedUri(curi) then
            return value, curi
        end
        return getNext()
    end

    return getNext
end

---@param scp scope
local function eachOfLinked(nameCollect, scp)
    local curi, value

    local function getNext()
        curi, value = next(nameCollect, curi)
        if not curi then
            return nil, nil
        end
        if  scp:isChildUri(curi)
        and scp:isLinkedUri(curi) then
            return value, curi
        end

        local cscp =   scope.getFolder(curi)
                    or scope.getLinkedScope(curi)
                    or scope.fallback

        if cscp == scp
        or cscp:isChildUri(scp.uri)
        or cscp:isLinkedUri(scp.uri) then
            return value, curi
        end

        return getNext()
    end

    return getNext
end

---@param scp scope
local function eachOfFallback(nameCollect, scp)
    local curi, value

    local function getNext()
        curi, value = next(nameCollect, curi)
        if not curi then
            return nil, nil
        end
        if scp:isLinkedUri(curi) then
            return value, curi
        end

        local cscp =   scope.getFolder(curi)
                    or scope.getLinkedScope(curi)
                    or scope.fallback

        if cscp == scp then
            return value, curi
        end

        return getNext()
    end

    return getNext
end

--- 迭代某个名字的订阅
---@param uri  uri
---@param name string
function mt:each(uri, name)
    uri = uri or '<fallback>'
    local nameCollect = self.collect[name]
    if not nameCollect then
        return DUMMY_FUNCTION
    end

    local scp = scope.getFolder(uri)

    if scp then
        return eachOfFolder(nameCollect, scp)
    end

    scp = scope.getLinkedScope(uri)

    if scp then
        return eachOfLinked(nameCollect, scp)
    end

    return eachOfFallback(nameCollect, scope.fallback)
end

local collectors = {}

local function new()
    return setmetatable({
        collect    = {},
        subscribed = {},
    }, mt)
end

---@return collector
return function (name)
    if name then
        collectors[name] = collectors[name] or new()
        return collectors[name]
    else
        return new()
    end
end