-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathproxy_table.lua
More file actions
176 lines (161 loc) · 6.44 KB
/
Copy pathproxy_table.lua
File metadata and controls
176 lines (161 loc) · 6.44 KB
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
--- allows the creation of a "proxy" for a real table or structure of tables which prevents modification but inheriting the values of the parent table.
--
-- constructor accepts a custom function for handling attempts to change the value of fields. Defaults to an error message.
--
-- This works with a given tree of tables. But note it does not protect classes within the table from being modified.
--
-- @module class.proxy_table
leef.class.proxy_table = {}
local proxy_table = leef.class.proxy_table
--reference which indicates that a value is nil. This is so that proxies can store overwritten data.
local NIL_VALUE = {}
--these are just to keep references around, if a reference is broken or lost on either side dont keep it.
proxy_table.objects_by_proxy = {}
local objects_by_proxy = proxy_table.objects_by_proxy
setmetatable(objects_by_proxy, {
__mode = "kv"
})
--- the proxy's overriden values and child tables
-- @field p.__LEEF_PROXY_OVERRIDES
local __LEEF_PROXY_OVERRIDES = "__LEEF_PROXY_OVERRIDES"
--- the proxy's parent table that it gets its values from
-- @field p.__LEEF_PROXY_PARENT
--local __LEEF_PROXY_PARENT = "__LEEF_PROXY_PARENT"
--- the proxy's index handling function
-- @field p.__LEEF_NEWINDEX_HANDLER
local __LEEF_NEWINDEX_HANDLER = "__LEEF_NEWINDEX_HANDLER"
local proxy_metatable = {
__index = function(proxy, key)
local overrides = rawget(proxy, __LEEF_PROXY_OVERRIDES)
local override_value = overrides[key]
local original_value = objects_by_proxy[proxy][key]
local out_value = override_value or original_value
local value_type = type(out_value)
--if it's overriden...
if override_value then
if override_value==NIL_VALUE then return nil end
return override_value
end
--if it's not overriden
if (value_type == "table") then
local new = proxy_table.new(out_value, rawget(proxy, __LEEF_NEWINDEX_HANDLER))
rawset(overrides, key, new)
return new
else
return original_value
end
end,
__newindex = function(t,k,v)
if rawget(t, __LEEF_NEWINDEX_HANDLER) then
t:__LEEF_NEWINDEX_HANDLER(objects_by_proxy[t], k, v)
else
error("attempt to modify proxy table.. "..debug.getinfo(2).short_src..":"..debug.getinfo(2).currentline)
end
end
}
--- sets an override on the proxy's fields. You can use this in the `newindex_handling_func` param to allow proxy tables to be modified
-- @tparam proxy proxy the proxy table
-- @param key
-- @param value
function proxy_table.set_field_override(proxy,...)
--if value==nil then value=NIL_VALUE end
local value_type = type(value)
if value==nil then
rawget(proxy, __LEEF_PROXY_OVERRIDES)[key] = NIL_VALUE
elseif value_type=="table" then
rawget(proxy, __LEEF_PROXY_OVERRIDES)[key] = proxy_table.new(value, rawget(proxy, __LEEF_NEWINDEX_HANDLER))
else
rawget(proxy, __LEEF_PROXY_OVERRIDES)[key] = value
end
end
function proxy_table.redact_field_override(t,k,v)
rawget(t, __LEEF_PROXY_OVERRIDES)[k] = nil
end
--- create a new proxy table
-- @tparam table table to create immutable interface for
-- @tparam functions newindex_handling_func `function(proxy, original_object, key, value)` to call when the proxy table has an attempted set
-- @return Proxy table
-- @function new
function proxy_table.new(tbl, newindex_handling_func)
assert(tbl~=proxy_table, "do not call leef.class.proxy_table functions as methods.")
assert(tbl, "no table provided")
--I'm leaving this here as a reminder: this will break everything because multiple proxies can exist for one table.
--And daully the best way to identify the existence of a proxy in any given structure (from it's parent) is it's presence in the table.
--and since __index only calls if something is NOT found in a table, this of course means that we simply should create it ourselves.
--> local proxy = proxies_by_object[tbl]
local proxy = {}
proxy.__LEEF_PROXY_PARENT = tbl
proxy.__LEEF_NEWINDEX_HANDLER = newindex_handling_func
proxy.__LEEF_PROXY_OVERRIDES = {}
--if it already existed there's no need to update any of this
--proxies_by_object[tbl] = proxy
objects_by_proxy[proxy] = tbl
setmetatable(proxy, proxy_metatable)
return proxy
end
--- check if its a proxy table
-- @param value value it check if proxy
-- @treturn bool
-- @function is_proxy
function proxy_table.is_proxy(value)
assert(value~=proxy_table, "do not call leef.class.proxy_table functions as methods.")
if objects_by_proxy[value] then return true end
return false
end
local function or_equals(a,b,c)
return (a==b) or (a==c)
end
local function proxy_pairs_iterator(overrides,k,original,iterating_proxy,proxy)
local outkey, _ = k, nil
if iterating_proxy then
local outval
outkey, outval = next(overrides, outkey)
if outval == NIL_VALUE then outkey, _, _ = proxy_pairs_iterator(overrides, outkey, original, true, proxy) end
if not outkey then outkey, _, iterating_proxy = proxy_pairs_iterator(overrides, nil, original, false, proxy) end
else
outkey, _ = next(original, outkey)
local temp_val = rawget(overrides,outkey)
if not outkey then return nil,nil,false end
if temp_val then outkey, _, _ = proxy_pairs_iterator(overrides, outkey, original, false, proxy) end
end
return outkey, outkey and proxy[outkey], iterating_proxy
end
--since next is modified we basically just return the normal pairs func to not kill perf
local old_pairs = pairs
function pairs(...)
local t = ...
local original = objects_by_proxy[t]
if original then
local key, val, mode = nil,nil,true
local overrides, proxy
return function(t2,k)
if not t2 then return end
overrides = overrides or rawget(t2, __LEEF_PROXY_OVERRIDES)
proxy = proxy or t2
key, val, mode = proxy_pairs_iterator(overrides, k, original, mode, proxy)
return key, val
end, t, nil
end
return old_pairs(...)
end
local function iter(p, i)
i = i + 1
local t = objects_by_proxy[p]
local v = t[i]
if v then
return i, v
end
end
local old_ipairs = ipairs
function ipairs(...)
local p = ...
if objects_by_proxy[p] then
return iter, p, 0
else
return old_ipairs(...)
end
end
--[[local old_ipairs = ipairs
function ipairs(t, ...)
return old_ipairs(proxies[t] or t, ...)
end]]