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