-
Notifications
You must be signed in to change notification settings - Fork 0
/
ICaseTable.lua
151 lines (124 loc) · 4.44 KB
/
ICaseTable.lua
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
---@class ICaseTable : table<string, any>
ICaseTable = {}
--- Creates a table with case insensitive string lookup.
--- Table can be used to store and lookup non-string keys as well.
--- Keys are case preserved while iterating (calling pairs).
--- The case will be updated after a set operation.
---
--- E.g:
--- * keys "ABC", "abc" and "aBc" are all considered the same key.
--- * Setting "ABC", "abc" then "aBc" the case returned when iterating will be "aBc".
---@param data? table @An optional table with an initial set of data
---@return ICaseTable|table<string, any> @A table with case insensitive lookup.
function CreateIcaseTable(data)
-- For case preservation.
local lookup = {}
-- Stores the values so we can properly update. We need the main table to always return nil on lookup
-- so __newindex is called when setting a value. If this doesn't happen then a case change (FOO to foo)
-- won't happen because __newindex is only called when the lookup fails (there isn't a metamethod for
-- lookup we can use).
local values = {}
local template = {}
local function __genOrderedIndex()
local orderedIndex = {}
for key in pairs(values) do
table.insert(orderedIndex, key)
end
table.sort(orderedIndex)
return orderedIndex
end
---@param t table
---@param state string
local function __orderedNext(t, state)
local key = nil
if (state == nil) then
-- the first time, generate the index
t.__orderedIndex = __genOrderedIndex()
key = t.__orderedIndex[1]
else
-- fetch the next value
for i = 1,#(t.__orderedIndex) do
if t.__orderedIndex[i] == state:lower() then
key = t.__orderedIndex[i + 1]
end
end
end
if (key) then
return lookup[key] or key, values[key]
end
-- no more value to return, cleanup
t.__orderedIndex = nil
end
function template.ordered()
return __orderedNext, template, nil
end
---@param t table
---@param state string
local function __nextPairs(t, state)
if (state ~= nil) then
-- Check that strings that have been normalized exist in the table.
if type(state) == "string" and values[state:lower()] ~= nil then
state = state:lower()
end
-- Ensure the value exists in the table.
if values[state] == nil then
return nil
end
end
local key,value = next(values, state)
return lookup[key] or key, value
end
function template.pairs()
return __nextPairs, template, nil
end
local mt = {
__index=function(t, k)
local v = nil
if type(k) == "string" then
-- Try to get the value for the key normalized.
v = values[k:lower()]
end
if v == nil then
v = values[k]
end
return v
end,
__newindex=function(t, k, v)
-- Store all strings normalized as lowercase.
if type(k) == "string" then
lookup[k:lower()] = v ~= nil and k or nil -- Clear the lookup value if we're setting to nil.
k = k:lower()
end
values[k] = v
end,
-- __pairs=function(t)
-- local function n(t, i)
-- if i ~= nil then
-- -- Check that strings that have been normalized exist in the table.
-- if type(i) == "string" and values[i:lower()] ~= nil then
-- i = i:lower()
-- end
-- -- Ensure the value exists in the table.
-- if values[i] == nil then
-- return nil
-- end
-- end
-- local k,v = next(values, i)
-- return lookup[k] or k, v
-- end
-- return n, t, nil
-- end
}
local modified = setmetatable(template, mt)
if (data and type(data) == "table") then
for key, value in pairs(data) do
modified[key] = value
end
end
return modified
end
--- Returns an iterator sorted by the keys
function ICaseTable.ordered() end
--- Returns an iterator
function ICaseTable.pairs() end
return ICaseTable