1
+ local RBXEnum = Enum
2
+ local EnumInstance = setmetatable ({}, {__tostring = function () return ' Enum' end , __metatable = ' this metatable is locked' , __newindex = function (self , key : string ) error ((" %q is read only" ):format (tostring (self ))) end , __index = function (self , key : string ) error ((' %s is not a valid member of %q' ):format (key , tostring (self )), 2 ) end })
3
+
4
+ local RegisteredMember = {
5
+ Enums = {},
6
+ UniqueNames = {},
7
+ RegisteredValues = {}
8
+ }
9
+
10
+ local HASH_SCALE = script :GetAttribute (' HashScale' ) or 16
11
+
12
+ local function isEnum (enum : Enum )
13
+ if typeof (enum ) == ' Enum' then
14
+ return true
15
+ end
16
+
17
+ if getmetatable (enum ) == EnumInstance then
18
+ return true
19
+ end
20
+
21
+ return false
22
+ end
23
+
24
+ local function isEnumItem (enumItem : EnumItem )
25
+ if typeof (enumItem ) == ' EnumItem' then
26
+ return true
27
+ end
28
+
29
+ if typeof (enumItem ) == ' number' then
30
+ local value
31
+ local completed = pcall (function ()
32
+ value = GetEnumFromValue (enumItem )
33
+ end )
34
+ return completed == true and value ~= nil
35
+ end
36
+
37
+ return false
38
+ end
39
+
40
+ local function IsNumberic (key : any )
41
+ return typeof (key ) == ' number'
42
+ end
43
+
44
+ local function HashString (name : string ) : number
45
+ local hash = 0
46
+ for i = 1 , # name do
47
+ hash = hash + string.byte (name , i )
48
+ end
49
+
50
+ return math.floor (hash * HASH_SCALE )
51
+ end
52
+
53
+ local function newEnum <A > (enumName : string , enumItems : A )
54
+ if RegisteredMember .UniqueNames [enumName ] then
55
+ error ((' Enum %q is registered' ):format (enumName ), 2 )
56
+ end
57
+
58
+ local hash = HashString (enumName )
59
+
60
+ export type StringName = keyof <A>
61
+
62
+ local enum = {}
63
+ local added_values = {}
64
+ local value_to_hash = {}
65
+ local InternalEnumItems = {}
66
+ local max_hash = hash
67
+ local value_name = {}
68
+ local item_count = 0
69
+
70
+ function enum :GetEnumItems () : {StringName }
71
+ return InternalEnumItems
72
+ end
73
+
74
+ function enum :FromName (name : StringName ) : number
75
+ return self [name ]
76
+ end
77
+
78
+ function enum :FromValue (value : number ) : number
79
+ for key , hashValue in pairs (value_to_hash ) do
80
+ if key == value or hashValue == value then
81
+ return hashValue
82
+ end
83
+ end
84
+
85
+ error ((' %s is not a valid member of %q' ):format (value , tostring (self )), 2 )
86
+ end
87
+
88
+ function enum :GetNameFromValue (value : number ) : string
89
+ local value = self :FromValue (value )
90
+ if value then
91
+ return value_name [value ]
92
+ end
93
+ end
94
+
95
+
96
+
97
+ local Enum = setmetatable ({}, {
98
+ __index = function (self , key : string )
99
+ local value = enum [key ]
100
+ if value == nil then
101
+ error ((' %s is not a valid member of %q' ):format (key , tostring (self )), 2 )
102
+ end
103
+
104
+ return value
105
+ end ,
106
+
107
+ __newindex = function (self , key : string )
108
+ local value = enum [key ]
109
+ if value ~= nil then
110
+ error ((' Unable to assign property %s. Property is read only' ):format (key ), 2 )
111
+ end
112
+
113
+ error ((' %s is not a valid member of %q' ):format (key , tostring (self )), 2 )
114
+ end ,
115
+
116
+ __tostring = function ()
117
+ return enumName
118
+ end ,
119
+
120
+ __len = function ()
121
+ return item_count
122
+ end ,
123
+
124
+ __metatable = EnumInstance
125
+ })
126
+
127
+
128
+ export type Enum = typeof (enum )
129
+
130
+ local idx = 0
131
+ local max = math.floor (hash / HASH_SCALE ) - 1
132
+
133
+ for key , value in pairs (enumItems ) do
134
+ local name = key
135
+ local enumValue = idx + 1
136
+ if IsNumberic (key ) then
137
+ name = value
138
+ else
139
+ if value > 0 then
140
+
141
+ if value > max then
142
+ error ((' %s: %q value is out of range. It should be between 1 and %d' ):format (enumName , name , max ), 2 )
143
+ end
144
+ enumValue = math .clamp (value , 1 , max )
145
+ end
146
+ end
147
+
148
+ if InternalEnumItems [name ] then
149
+ warn ((' EnumItem %q is a duplicate and will be ignored for %q' ):format (name , enumName ), 2 )
150
+ continue
151
+ end
152
+
153
+ while added_values [enumValue ] and enumValue <= max do
154
+ enumValue += 1
155
+ end
156
+
157
+ added_values [enumValue ] = true
158
+ max_hash = (hash + enumValue ) - 1
159
+ enum [name ] = max_hash
160
+
161
+ if RegisteredMember .RegisteredValues [max_hash ] then
162
+ error ((' %s: EnumItem %q hash is already registered by another EnumItem' ):format (enumName , name ), 2 )
163
+ end
164
+
165
+ value_to_hash [enumValue ] = max_hash
166
+ RegisteredMember .RegisteredValues [max_hash ] = Enum
167
+ table.insert (InternalEnumItems , name )
168
+ item_count += 1
169
+
170
+ value_name [max_hash ] = name
171
+ value_name [enumValue ] = name
172
+
173
+ idx = enumValue
174
+ end
175
+
176
+
177
+ table.insert (RegisteredMember .Enums , Enum )
178
+ RegisteredMember .UniqueNames [enumName ] = true
179
+
180
+ return Enum :: Enum & A
181
+ end
182
+
183
+ local Def = {}
184
+ export type Enum = typeof (newEnum (" Enum" , Def ))
185
+
186
+
187
+ function GetRegisteredEnums () : {[number ]: Enum }
188
+ return RegisteredMember .Enums
189
+ end
190
+
191
+ function GetEnumFromValue (value : number ) : Enum
192
+ local Enum = RegisteredMember .RegisteredValues [value ]
193
+ if Enum == nil then
194
+ error ((' %s is not a registered member of Enum' ):format (value ), 2 )
195
+ end
196
+
197
+ return Enum
198
+ end
199
+
200
+ return {
201
+ new = newEnum ,
202
+ isEnum = isEnum ,
203
+ isEnumItem = isEnumItem ,
204
+ RBXEnum = RBXEnum ,
205
+ getEnumFromValue = GetEnumFromValue ,
206
+ getRegisteredEnums = GetRegisteredEnums
207
+ }
0 commit comments