roblox-opencode 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (246) hide show
  1. package/README.md +112 -122
  2. package/commands/setup-game.md +108 -108
  3. package/commands/sync-check.md +53 -53
  4. package/core/roblox-core.md +93 -93
  5. package/dist/server.js +189 -167
  6. package/package.json +35 -35
  7. package/skills/roblox-analytics/SKILL.md +277 -277
  8. package/skills/roblox-analytics/references/event-batcher.luau +75 -75
  9. package/skills/roblox-animation-vfx/SKILL.md +1325 -1325
  10. package/skills/roblox-architecture/SKILL.md +863 -863
  11. package/skills/roblox-architecture/references/combat-systems.md +1381 -1381
  12. package/skills/roblox-code-review/SKILL.md +686 -686
  13. package/skills/roblox-data/SKILL.md +889 -889
  14. package/skills/roblox-data/references/inventory-systems.md +1729 -1729
  15. package/skills/roblox-debug/SKILL.md +98 -98
  16. package/skills/roblox-gui/SKILL.md +1103 -1103
  17. package/skills/roblox-gui-fusion/SKILL.md +150 -150
  18. package/skills/roblox-gui-fusion/references/inventory.luau +427 -427
  19. package/skills/roblox-gui-fusion/references/settings-menu.luau +579 -579
  20. package/skills/roblox-gui-fusion/references/shop.luau +411 -411
  21. package/skills/roblox-luau-mastery/SKILL.md +1519 -1519
  22. package/skills/roblox-monetization/SKILL.md +1084 -1084
  23. package/skills/roblox-monetization/references/process-receipt.luau +131 -131
  24. package/skills/roblox-networking/SKILL.md +669 -669
  25. package/skills/roblox-networking/references/remote-validator.luau +193 -193
  26. package/skills/roblox-publish-checklist/SKILL.md +127 -127
  27. package/skills/roblox-runtime/SKILL.md +753 -753
  28. package/skills/roblox-sharp-edges/SKILL.md +294 -294
  29. package/skills/roblox-sync/SKILL.md +126 -126
  30. package/skills/roblox-testing/SKILL.md +943 -943
  31. package/skills/roblox-tooling/SKILL.md +149 -149
  32. package/vendor/LICENSES/ProfileStore-LICENSE +201 -201
  33. package/vendor/LICENSES/RbxUtil-LICENSE +7 -7
  34. package/vendor/LICENSES/promise-LICENSE +20 -20
  35. package/vendor/LICENSES/t-LICENSE +21 -21
  36. package/vendor/LICENSES/testez-LICENSE +200 -200
  37. package/vendor/README.md +83 -83
  38. package/vendor/fusion/Animation/ExternalTime.luau +83 -83
  39. package/vendor/fusion/Animation/Spring.luau +321 -321
  40. package/vendor/fusion/Animation/Stopwatch.luau +127 -127
  41. package/vendor/fusion/Animation/Tween.luau +187 -187
  42. package/vendor/fusion/Animation/getTweenDuration.luau +27 -27
  43. package/vendor/fusion/Animation/getTweenRatio.luau +47 -47
  44. package/vendor/fusion/Animation/lerpType.luau +163 -163
  45. package/vendor/fusion/Animation/packType.luau +99 -99
  46. package/vendor/fusion/Animation/springCoefficients.luau +80 -80
  47. package/vendor/fusion/Animation/unpackType.luau +102 -102
  48. package/vendor/fusion/Colour/Oklab.luau +70 -70
  49. package/vendor/fusion/Colour/sRGB.luau +54 -54
  50. package/vendor/fusion/External.luau +167 -167
  51. package/vendor/fusion/ExternalDebug.luau +69 -69
  52. package/vendor/fusion/Graph/Observer.luau +113 -113
  53. package/vendor/fusion/Graph/castToGraph.luau +28 -28
  54. package/vendor/fusion/Graph/change.luau +80 -80
  55. package/vendor/fusion/Graph/depend.luau +32 -32
  56. package/vendor/fusion/Graph/evaluate.luau +55 -55
  57. package/vendor/fusion/Instances/Attribute.luau +57 -57
  58. package/vendor/fusion/Instances/AttributeChange.luau +46 -46
  59. package/vendor/fusion/Instances/AttributeOut.luau +63 -63
  60. package/vendor/fusion/Instances/Child.luau +21 -21
  61. package/vendor/fusion/Instances/Children.luau +147 -147
  62. package/vendor/fusion/Instances/Hydrate.luau +32 -32
  63. package/vendor/fusion/Instances/New.luau +52 -52
  64. package/vendor/fusion/Instances/OnChange.luau +49 -49
  65. package/vendor/fusion/Instances/OnEvent.luau +53 -53
  66. package/vendor/fusion/Instances/Out.luau +69 -69
  67. package/vendor/fusion/Instances/applyInstanceProps.luau +148 -148
  68. package/vendor/fusion/Instances/defaultProps.luau +194 -194
  69. package/vendor/fusion/LICENSE +21 -21
  70. package/vendor/fusion/Logging/formatError.luau +48 -48
  71. package/vendor/fusion/Logging/messages.luau +51 -51
  72. package/vendor/fusion/Logging/parseError.luau +24 -24
  73. package/vendor/fusion/Memory/checkLifetime.luau +133 -133
  74. package/vendor/fusion/Memory/deriveScope.luau +23 -23
  75. package/vendor/fusion/Memory/deriveScopeImpl.luau +44 -44
  76. package/vendor/fusion/Memory/doCleanup.luau +78 -78
  77. package/vendor/fusion/Memory/innerScope.luau +33 -33
  78. package/vendor/fusion/Memory/legacyCleanup.luau +17 -17
  79. package/vendor/fusion/Memory/needsDestruction.luau +16 -16
  80. package/vendor/fusion/Memory/poisonScope.luau +33 -33
  81. package/vendor/fusion/Memory/scopePool.luau +54 -54
  82. package/vendor/fusion/Memory/scoped.luau +26 -26
  83. package/vendor/fusion/Memory/whichLivesLonger.luau +74 -74
  84. package/vendor/fusion/RobloxExternal.luau +97 -97
  85. package/vendor/fusion/State/Computed.luau +138 -138
  86. package/vendor/fusion/State/For/Disassembly.luau +210 -210
  87. package/vendor/fusion/State/For/ForTypes.luau +30 -30
  88. package/vendor/fusion/State/For/init.luau +109 -109
  89. package/vendor/fusion/State/ForKeys.luau +93 -93
  90. package/vendor/fusion/State/ForPairs.luau +96 -96
  91. package/vendor/fusion/State/ForValues.luau +93 -93
  92. package/vendor/fusion/State/Value.luau +87 -87
  93. package/vendor/fusion/State/castToState.luau +25 -25
  94. package/vendor/fusion/State/peek.luau +30 -30
  95. package/vendor/fusion/Types.luau +314 -314
  96. package/vendor/fusion/Utility/Contextual.luau +90 -90
  97. package/vendor/fusion/Utility/Safe.luau +22 -22
  98. package/vendor/fusion/Utility/isSimilar.luau +29 -29
  99. package/vendor/fusion/Utility/merge.luau +35 -35
  100. package/vendor/fusion/Utility/nameOf.luau +34 -34
  101. package/vendor/fusion/Utility/never.luau +13 -13
  102. package/vendor/fusion/Utility/nicknames.luau +10 -10
  103. package/vendor/fusion/Utility/xtypeof.luau +26 -26
  104. package/vendor/fusion/init.luau +82 -82
  105. package/vendor/profilestore/init.luau +2242 -2242
  106. package/vendor/promise/init.luau +1982 -1982
  107. package/vendor/rbxutil/buffer-util/Buffer.test.luau +25 -25
  108. package/vendor/rbxutil/buffer-util/BufferReader.luau +228 -228
  109. package/vendor/rbxutil/buffer-util/BufferWriter.luau +269 -269
  110. package/vendor/rbxutil/buffer-util/DataTypeBuffer.luau +223 -223
  111. package/vendor/rbxutil/buffer-util/Types.luau +60 -60
  112. package/vendor/rbxutil/buffer-util/index.d.ts +153 -153
  113. package/vendor/rbxutil/buffer-util/init.luau +41 -41
  114. package/vendor/rbxutil/buffer-util/package.json +16 -16
  115. package/vendor/rbxutil/buffer-util/wally.toml +9 -9
  116. package/vendor/rbxutil/comm/Client/ClientComm.luau +232 -232
  117. package/vendor/rbxutil/comm/Client/ClientRemoteProperty.luau +156 -156
  118. package/vendor/rbxutil/comm/Client/ClientRemoteSignal.luau +109 -109
  119. package/vendor/rbxutil/comm/Client/init.luau +135 -135
  120. package/vendor/rbxutil/comm/Server/RemoteProperty.luau +295 -295
  121. package/vendor/rbxutil/comm/Server/RemoteSignal.luau +211 -211
  122. package/vendor/rbxutil/comm/Server/ServerComm.luau +211 -211
  123. package/vendor/rbxutil/comm/Server/init.luau +140 -140
  124. package/vendor/rbxutil/comm/Types.luau +18 -18
  125. package/vendor/rbxutil/comm/Util.luau +27 -27
  126. package/vendor/rbxutil/comm/init.luau +35 -35
  127. package/vendor/rbxutil/comm/wally.toml +13 -13
  128. package/vendor/rbxutil/component/init.luau +759 -759
  129. package/vendor/rbxutil/component/init.test.luau +311 -311
  130. package/vendor/rbxutil/component/wally.toml +14 -14
  131. package/vendor/rbxutil/concur/init.luau +542 -542
  132. package/vendor/rbxutil/concur/init.test.luau +364 -364
  133. package/vendor/rbxutil/concur/wally.toml +8 -8
  134. package/vendor/rbxutil/enum-list/init.luau +101 -101
  135. package/vendor/rbxutil/enum-list/init.test.luau +91 -91
  136. package/vendor/rbxutil/enum-list/wally.toml +8 -8
  137. package/vendor/rbxutil/find/index.d.ts +20 -20
  138. package/vendor/rbxutil/find/init.luau +44 -44
  139. package/vendor/rbxutil/find/package.json +17 -17
  140. package/vendor/rbxutil/find/wally.toml +8 -8
  141. package/vendor/rbxutil/input/Gamepad.luau +559 -559
  142. package/vendor/rbxutil/input/Keyboard.luau +124 -124
  143. package/vendor/rbxutil/input/Mouse.luau +278 -278
  144. package/vendor/rbxutil/input/PreferredInput.luau +91 -91
  145. package/vendor/rbxutil/input/Touch.luau +120 -120
  146. package/vendor/rbxutil/input/init.luau +33 -33
  147. package/vendor/rbxutil/input/wally.toml +12 -12
  148. package/vendor/rbxutil/loader/index.d.ts +15 -15
  149. package/vendor/rbxutil/loader/init.luau +137 -137
  150. package/vendor/rbxutil/loader/wally.toml +8 -8
  151. package/vendor/rbxutil/log/index.d.ts +38 -38
  152. package/vendor/rbxutil/log/init.luau +746 -746
  153. package/vendor/rbxutil/log/wally.toml +8 -8
  154. package/vendor/rbxutil/net/init.luau +190 -190
  155. package/vendor/rbxutil/net/wally.toml +8 -8
  156. package/vendor/rbxutil/option/index.d.ts +44 -44
  157. package/vendor/rbxutil/option/init.luau +489 -489
  158. package/vendor/rbxutil/option/init.test.luau +342 -342
  159. package/vendor/rbxutil/option/wally.toml +8 -8
  160. package/vendor/rbxutil/pid/index.d.ts +53 -53
  161. package/vendor/rbxutil/pid/init.luau +195 -195
  162. package/vendor/rbxutil/pid/package.json +16 -16
  163. package/vendor/rbxutil/pid/wally.toml +9 -9
  164. package/vendor/rbxutil/quaternion/index.d.ts +117 -117
  165. package/vendor/rbxutil/quaternion/init.luau +570 -570
  166. package/vendor/rbxutil/quaternion/package.json +16 -16
  167. package/vendor/rbxutil/quaternion/wally.toml +9 -9
  168. package/vendor/rbxutil/query/index.d.ts +43 -43
  169. package/vendor/rbxutil/query/init.luau +117 -117
  170. package/vendor/rbxutil/query/package.json +18 -18
  171. package/vendor/rbxutil/query/wally.toml +9 -9
  172. package/vendor/rbxutil/sequent/index.d.ts +28 -28
  173. package/vendor/rbxutil/sequent/init.luau +340 -340
  174. package/vendor/rbxutil/sequent/package.json +16 -16
  175. package/vendor/rbxutil/sequent/wally.toml +9 -9
  176. package/vendor/rbxutil/ser/init.luau +175 -175
  177. package/vendor/rbxutil/ser/init.test.luau +50 -50
  178. package/vendor/rbxutil/ser/wally.toml +11 -11
  179. package/vendor/rbxutil/shake/index.d.ts +36 -36
  180. package/vendor/rbxutil/shake/init.luau +532 -532
  181. package/vendor/rbxutil/shake/init.test.luau +267 -267
  182. package/vendor/rbxutil/shake/package.json +16 -16
  183. package/vendor/rbxutil/shake/wally.toml +9 -9
  184. package/vendor/rbxutil/signal/index.d.ts +100 -100
  185. package/vendor/rbxutil/signal/init.luau +432 -432
  186. package/vendor/rbxutil/signal/init.test.luau +190 -190
  187. package/vendor/rbxutil/signal/package.json +17 -17
  188. package/vendor/rbxutil/signal/wally.toml +9 -9
  189. package/vendor/rbxutil/silo/TableWatcher.luau +65 -65
  190. package/vendor/rbxutil/silo/Util.luau +55 -55
  191. package/vendor/rbxutil/silo/init.luau +338 -338
  192. package/vendor/rbxutil/silo/init.test.luau +215 -215
  193. package/vendor/rbxutil/silo/wally.toml +8 -8
  194. package/vendor/rbxutil/spring/index.d.ts +40 -40
  195. package/vendor/rbxutil/spring/init.luau +97 -97
  196. package/vendor/rbxutil/spring/package.json +17 -17
  197. package/vendor/rbxutil/spring/wally.toml +8 -8
  198. package/vendor/rbxutil/stream/index.d.ts +88 -88
  199. package/vendor/rbxutil/stream/init.luau +597 -597
  200. package/vendor/rbxutil/stream/package.json +18 -18
  201. package/vendor/rbxutil/stream/wally.toml +9 -9
  202. package/vendor/rbxutil/streamable/Streamable.luau +202 -202
  203. package/vendor/rbxutil/streamable/StreamableUtil.luau +80 -80
  204. package/vendor/rbxutil/streamable/init.luau +8 -8
  205. package/vendor/rbxutil/streamable/wally.toml +12 -12
  206. package/vendor/rbxutil/symbol/init.luau +56 -56
  207. package/vendor/rbxutil/symbol/init.test.luau +37 -37
  208. package/vendor/rbxutil/symbol/wally.toml +8 -8
  209. package/vendor/rbxutil/table-util/init.luau +938 -938
  210. package/vendor/rbxutil/table-util/init.test.luau +439 -439
  211. package/vendor/rbxutil/task-queue/index.d.ts +27 -27
  212. package/vendor/rbxutil/task-queue/init.luau +97 -97
  213. package/vendor/rbxutil/task-queue/wally.toml +8 -8
  214. package/vendor/rbxutil/timer/index.d.ts +81 -81
  215. package/vendor/rbxutil/timer/init.luau +249 -249
  216. package/vendor/rbxutil/timer/init.test.luau +73 -73
  217. package/vendor/rbxutil/timer/wally.toml +11 -11
  218. package/vendor/rbxutil/tree/index.d.ts +15 -15
  219. package/vendor/rbxutil/tree/init.luau +137 -137
  220. package/vendor/rbxutil/tree/wally.toml +8 -8
  221. package/vendor/rbxutil/trove/index.d.ts +46 -46
  222. package/vendor/rbxutil/trove/init.luau +787 -787
  223. package/vendor/rbxutil/trove/init.test.luau +203 -203
  224. package/vendor/rbxutil/trove/wally.toml +8 -8
  225. package/vendor/rbxutil/typed-remote/init.luau +196 -196
  226. package/vendor/rbxutil/typed-remote/wally.toml +8 -8
  227. package/vendor/rbxutil/wait-for/index.d.ts +17 -17
  228. package/vendor/rbxutil/wait-for/init.luau +257 -257
  229. package/vendor/rbxutil/wait-for/init.test.luau +182 -182
  230. package/vendor/rbxutil/wait-for/wally.toml +11 -11
  231. package/vendor/t/t.lua +1350 -1350
  232. package/vendor/testez/Context.lua +26 -26
  233. package/vendor/testez/Expectation.lua +311 -311
  234. package/vendor/testez/ExpectationContext.lua +38 -38
  235. package/vendor/testez/LifecycleHooks.lua +89 -89
  236. package/vendor/testez/Reporters/TeamCityReporter.lua +101 -101
  237. package/vendor/testez/Reporters/TextReporter.lua +105 -105
  238. package/vendor/testez/Reporters/TextReporterQuiet.lua +96 -96
  239. package/vendor/testez/TestBootstrap.lua +146 -146
  240. package/vendor/testez/TestEnum.lua +27 -27
  241. package/vendor/testez/TestPlan.lua +304 -304
  242. package/vendor/testez/TestPlanner.lua +39 -39
  243. package/vendor/testez/TestResults.lua +111 -111
  244. package/vendor/testez/TestRunner.lua +188 -188
  245. package/vendor/testez/TestSession.lua +243 -243
  246. package/vendor/testez/init.lua +39 -39
@@ -1,489 +1,489 @@
1
- -- Option
2
- -- Stephen Leitnick
3
- -- August 28, 2020
4
-
5
- --[[
6
-
7
- MatchTable {
8
- Some: (value: any) -> any
9
- None: () -> any
10
- }
11
-
12
- CONSTRUCTORS:
13
-
14
- Option.Some(anyNonNilValue): Option<any>
15
- Option.Wrap(anyValue): Option<any>
16
-
17
-
18
- STATIC FIELDS:
19
-
20
- Option.None: Option<None>
21
-
22
-
23
- STATIC METHODS:
24
-
25
- Option.Is(obj): boolean
26
-
27
-
28
- METHODS:
29
-
30
- opt:Match(): (matches: MatchTable) -> any
31
- opt:IsSome(): boolean
32
- opt:IsNone(): boolean
33
- opt:Unwrap(): any
34
- opt:Expect(errMsg: string): any
35
- opt:ExpectNone(errMsg: string): void
36
- opt:UnwrapOr(default: any): any
37
- opt:UnwrapOrElse(default: () -> any): any
38
- opt:And(opt2: Option<any>): Option<any>
39
- opt:AndThen(predicate: (unwrapped: any) -> Option<any>): Option<any>
40
- opt:Or(opt2: Option<any>): Option<any>
41
- opt:OrElse(orElseFunc: () -> Option<any>): Option<any>
42
- opt:XOr(opt2: Option<any>): Option<any>
43
- opt:Contains(value: any): boolean
44
-
45
- --------------------------------------------------------------------
46
-
47
- Options are useful for handling nil-value cases. Any time that an
48
- operation might return nil, it is useful to instead return an
49
- Option, which will indicate that the value might be nil, and should
50
- be explicitly checked before using the value. This will help
51
- prevent common bugs caused by nil values that can fail silently.
52
-
53
-
54
- Example:
55
-
56
- local result1 = Option.Some(32)
57
- local result2 = Option.Some(nil)
58
- local result3 = Option.Some("Hi")
59
- local result4 = Option.Some(nil)
60
- local result5 = Option.None
61
-
62
- -- Use 'Match' to match if the value is Some or None:
63
- result1:Match {
64
- Some = function(value) print(value) end;
65
- None = function() print("No value") end;
66
- }
67
-
68
- -- Raw check:
69
- if result2:IsSome() then
70
- local value = result2:Unwrap() -- Explicitly call Unwrap
71
- print("Value of result2:", value)
72
- end
73
-
74
- if result3:IsNone() then
75
- print("No result for result3")
76
- end
77
-
78
- -- Bad, will throw error bc result4 is none:
79
- local value = result4:Unwrap()
80
-
81
- --]]
82
-
83
- export type MatchTable<T> = {
84
- Some: (value: T) -> any,
85
- None: () -> any,
86
- }
87
-
88
- export type MatchFn<T> = (matches: MatchTable<T>) -> any
89
-
90
- export type DefaultFn<T> = () -> T
91
-
92
- export type AndThenFn<T> = (value: T) -> Option<T>
93
-
94
- export type OrElseFn<T> = () -> Option<T>
95
-
96
- export type Option<T> = typeof(setmetatable(
97
- {} :: {
98
- Match: (self: Option<T>) -> MatchFn<T>,
99
- IsSome: (self: Option<T>) -> boolean,
100
- IsNone: (self: Option<T>) -> boolean,
101
- Contains: (self: Option<T>, value: T) -> boolean,
102
- Unwrap: (self: Option<T>) -> T,
103
- Expect: (self: Option<T>, errMsg: string) -> T,
104
- ExpectNone: (self: Option<T>, errMsg: string) -> nil,
105
- UnwrapOr: (self: Option<T>, default: T) -> T,
106
- UnwrapOrElse: (self: Option<T>, defaultFn: DefaultFn<T>) -> T,
107
- And: (self: Option<T>, opt2: Option<T>) -> Option<T>,
108
- AndThen: (self: Option<T>, predicate: AndThenFn<T>) -> Option<T>,
109
- Or: (self: Option<T>, opt2: Option<T>) -> Option<T>,
110
- OrElse: (self: Option<T>, orElseFunc: OrElseFn<T>) -> Option<T>,
111
- XOr: (self: Option<T>, opt2: Option<T>) -> Option<T>,
112
- },
113
- {} :: {
114
- __index: Option<T>,
115
- }
116
- ))
117
-
118
- local CLASSNAME = "Option"
119
-
120
- --[=[
121
- @class Option
122
-
123
- Represents an optional value in Lua. This is useful to avoid `nil` bugs, which can
124
- go silently undetected within code and cause hidden or hard-to-find bugs.
125
- ]=]
126
- local Option = {}
127
- Option.__index = Option
128
-
129
- function Option._new(value)
130
- local self = setmetatable({
131
- ClassName = CLASSNAME,
132
- _v = value,
133
- _s = (value ~= nil),
134
- }, Option)
135
- return self
136
- end
137
-
138
- --[=[
139
- @param value T
140
- @return Option<T>
141
-
142
- Creates an Option instance with the given value. Throws an error
143
- if the given value is `nil`.
144
- ]=]
145
- function Option.Some(value)
146
- assert(value ~= nil, "Option.Some() value cannot be nil")
147
- return Option._new(value)
148
- end
149
-
150
- --[=[
151
- @param value T
152
- @return Option<T> | Option<None>
153
-
154
- Safely wraps the given value as an option. If the
155
- value is `nil`, returns `Option.None`, otherwise
156
- returns `Option.Some(value)`.
157
- ]=]
158
- function Option.Wrap(value)
159
- if value == nil then
160
- return Option.None
161
- else
162
- return Option.Some(value)
163
- end
164
- end
165
-
166
- --[=[
167
- @param obj any
168
- @return boolean
169
- Returns `true` if `obj` is an Option.
170
- ]=]
171
- function Option.Is(obj)
172
- return type(obj) == "table" and getmetatable(obj) == Option
173
- end
174
-
175
- --[=[
176
- @param obj any
177
- Throws an error if `obj` is not an Option.
178
- ]=]
179
- function Option.Assert(obj)
180
- assert(Option.Is(obj), "Result was not of type Option")
181
- end
182
-
183
- --[=[
184
- @param data table
185
- @return Option
186
- Deserializes the data into an Option. This data should have come from
187
- the `option:Serialize()` method.
188
- ]=]
189
- function Option.Deserialize(data) -- type data = {ClassName: string, Value: any}
190
- assert(type(data) == "table" and data.ClassName == CLASSNAME, "Invalid data for deserializing Option")
191
- return data.Value == nil and Option.None or Option.Some(data.Value)
192
- end
193
-
194
- --[=[
195
- @return table
196
- Returns a serialized version of the option.
197
- ]=]
198
- function Option:Serialize()
199
- return {
200
- ClassName = self.ClassName,
201
- Value = self._v,
202
- }
203
- end
204
-
205
- --[=[
206
- @param matches {Some: (value: any) -> any, None: () -> any}
207
- @return any
208
-
209
- Matches against the option.
210
-
211
- ```lua
212
- local opt = Option.Some(32)
213
- opt:Match {
214
- Some = function(num) print("Number", num) end,
215
- None = function() print("No value") end,
216
- }
217
- ```
218
- ]=]
219
- function Option:Match(matches)
220
- local onSome = matches.Some
221
- local onNone = matches.None
222
- assert(type(onSome) == "function", "Missing 'Some' match")
223
- assert(type(onNone) == "function", "Missing 'None' match")
224
- if self:IsSome() then
225
- return onSome(self:Unwrap())
226
- else
227
- return onNone()
228
- end
229
- end
230
-
231
- --[=[
232
- @return boolean
233
- Returns `true` if the option has a value.
234
- ]=]
235
- function Option:IsSome()
236
- return self._s
237
- end
238
-
239
- --[=[
240
- @return boolean
241
- Returns `true` if the option is None.
242
- ]=]
243
- function Option:IsNone()
244
- return not self._s
245
- end
246
-
247
- --[=[
248
- @param msg string
249
- @return value: any
250
- Unwraps the value in the option, otherwise throws an error with `msg` as the error message.
251
- ```lua
252
- local opt = Option.Some(10)
253
- print(opt:Expect("No number")) -> 10
254
- print(Option.None:Expect("No number")) -- Throws an error "No number"
255
- ```
256
- ]=]
257
- function Option:Expect(msg)
258
- assert(self:IsSome(), msg)
259
- return self._v
260
- end
261
-
262
- --[=[
263
- @param msg string
264
- Throws an error with `msg` as the error message if the value is _not_ None.
265
- ]=]
266
- function Option:ExpectNone(msg)
267
- assert(self:IsNone(), msg)
268
- end
269
-
270
- --[=[
271
- @return value: any
272
- Returns the value in the option, or throws an error if the option is None.
273
- ]=]
274
- function Option:Unwrap()
275
- return self:Expect("Cannot unwrap option of None type")
276
- end
277
-
278
- --[=[
279
- @param default any
280
- @return value: any
281
- If the option holds a value, returns the value. Otherwise, returns `default`.
282
- ]=]
283
- function Option:UnwrapOr(default)
284
- if self:IsSome() then
285
- return self:Unwrap()
286
- else
287
- return default
288
- end
289
- end
290
-
291
- --[=[
292
- @param defaultFn () -> any
293
- @return value: any
294
- If the option holds a value, returns the value. Otherwise, returns the
295
- result of the `defaultFn` function.
296
- ]=]
297
- function Option:UnwrapOrElse(defaultFn)
298
- if self:IsSome() then
299
- return self:Unwrap()
300
- else
301
- return defaultFn()
302
- end
303
- end
304
-
305
- --[=[
306
- @param optionB Option
307
- @return Option
308
- Returns `optionB` if the calling option has a value,
309
- otherwise returns None.
310
-
311
- ```lua
312
- local optionA = Option.Some(32)
313
- local optionB = Option.Some(64)
314
- local opt = optionA:And(optionB)
315
- -- opt == optionB
316
-
317
- local optionA = Option.None
318
- local optionB = Option.Some(64)
319
- local opt = optionA:And(optionB)
320
- -- opt == Option.None
321
- ```
322
- ]=]
323
- function Option:And(optionB)
324
- if self:IsSome() then
325
- return optionB
326
- else
327
- return Option.None
328
- end
329
- end
330
-
331
- --[=[
332
- @param andThenFn (value: any) -> Option
333
- @return value: Option
334
- If the option holds a value, then the `andThenFn`
335
- function is called with the held value of the option,
336
- and then the resultant Option returned by the `andThenFn`
337
- is returned. Otherwise, None is returned.
338
-
339
- ```lua
340
- local optA = Option.Some(32)
341
- local optB = optA:AndThen(function(num)
342
- return Option.Some(num * 2)
343
- end)
344
- print(optB:Expect("Expected number")) --> 64
345
- ```
346
- ]=]
347
- function Option:AndThen(andThenFn)
348
- if self:IsSome() then
349
- local result = andThenFn(self:Unwrap())
350
- Option.Assert(result)
351
- return result
352
- else
353
- return Option.None
354
- end
355
- end
356
-
357
- --[=[
358
- @param optionB Option
359
- @return Option
360
- If caller has a value, returns itself. Otherwise, returns `optionB`.
361
- ]=]
362
- function Option:Or(optionB)
363
- if self:IsSome() then
364
- return self
365
- else
366
- return optionB
367
- end
368
- end
369
-
370
- --[=[
371
- @param orElseFn () -> Option
372
- @return Option
373
- If caller has a value, returns itself. Otherwise, returns the
374
- option generated by the `orElseFn` function.
375
- ]=]
376
- function Option:OrElse(orElseFn)
377
- if self:IsSome() then
378
- return self
379
- else
380
- local result = orElseFn()
381
- Option.Assert(result)
382
- return result
383
- end
384
- end
385
-
386
- --[=[
387
- @param optionB Option
388
- @return Option
389
- If both `self` and `optionB` have values _or_ both don't have a value,
390
- then this returns None. Otherwise, it returns the option that does have
391
- a value.
392
- ]=]
393
- function Option:XOr(optionB)
394
- local someOptA = self:IsSome()
395
- local someOptB = optionB:IsSome()
396
- if someOptA == someOptB then
397
- return Option.None
398
- elseif someOptA then
399
- return self
400
- else
401
- return optionB
402
- end
403
- end
404
-
405
- --[=[
406
- @param predicate (value: any) -> boolean
407
- @return Option
408
- Returns `self` if this option has a value and the predicate returns `true.
409
- Otherwise, returns None.
410
- ]=]
411
- function Option:Filter(predicate)
412
- if self:IsNone() or not predicate(self._v) then
413
- return Option.None
414
- else
415
- return self
416
- end
417
- end
418
-
419
- --[=[
420
- @param value any
421
- @return boolean
422
- Returns `true` if this option contains `value`.
423
- ]=]
424
- function Option:Contains(value)
425
- return self:IsSome() and self._v == value
426
- end
427
-
428
- --[=[
429
- @return string
430
- Metamethod to transform the option into a string.
431
- ```lua
432
- local optA = Option.Some(64)
433
- local optB = Option.None
434
- print(optA) --> Option<number>
435
- print(optB) --> Option<None>
436
- ```
437
- ]=]
438
- function Option:__tostring()
439
- if self:IsSome() then
440
- return ("Option<" .. typeof(self._v) .. ">")
441
- else
442
- return "Option<None>"
443
- end
444
- end
445
-
446
- --[=[
447
- @return boolean
448
- @param opt Option
449
- Metamethod to check equality between two options. Returns `true` if both
450
- options hold the same value _or_ both options are None.
451
- ```lua
452
- local o1 = Option.Some(32)
453
- local o2 = Option.Some(32)
454
- local o3 = Option.Some(64)
455
- local o4 = Option.None
456
- local o5 = Option.None
457
-
458
- print(o1 == o2) --> true
459
- print(o1 == o3) --> false
460
- print(o1 == o4) --> false
461
- print(o4 == o5) --> true
462
- ```
463
- ]=]
464
- function Option:__eq(opt)
465
- if Option.Is(opt) then
466
- if self:IsSome() and opt:IsSome() then
467
- return (self:Unwrap() == opt:Unwrap())
468
- elseif self:IsNone() and opt:IsNone() then
469
- return true
470
- end
471
- end
472
- return false
473
- end
474
-
475
- --[=[
476
- @prop None Option<None>
477
- @within Option
478
- Represents no value.
479
- ]=]
480
- Option.None = Option._new()
481
-
482
- return (Option :: any) :: {
483
- Some: <T>(value: T) -> Option<T>,
484
- Wrap: <T>(value: T?) -> Option<T>,
485
-
486
- Is: (obj: any) -> boolean,
487
-
488
- None: Option<any>,
489
- }
1
+ -- Option
2
+ -- Stephen Leitnick
3
+ -- August 28, 2020
4
+
5
+ --[[
6
+
7
+ MatchTable {
8
+ Some: (value: any) -> any
9
+ None: () -> any
10
+ }
11
+
12
+ CONSTRUCTORS:
13
+
14
+ Option.Some(anyNonNilValue): Option<any>
15
+ Option.Wrap(anyValue): Option<any>
16
+
17
+
18
+ STATIC FIELDS:
19
+
20
+ Option.None: Option<None>
21
+
22
+
23
+ STATIC METHODS:
24
+
25
+ Option.Is(obj): boolean
26
+
27
+
28
+ METHODS:
29
+
30
+ opt:Match(): (matches: MatchTable) -> any
31
+ opt:IsSome(): boolean
32
+ opt:IsNone(): boolean
33
+ opt:Unwrap(): any
34
+ opt:Expect(errMsg: string): any
35
+ opt:ExpectNone(errMsg: string): void
36
+ opt:UnwrapOr(default: any): any
37
+ opt:UnwrapOrElse(default: () -> any): any
38
+ opt:And(opt2: Option<any>): Option<any>
39
+ opt:AndThen(predicate: (unwrapped: any) -> Option<any>): Option<any>
40
+ opt:Or(opt2: Option<any>): Option<any>
41
+ opt:OrElse(orElseFunc: () -> Option<any>): Option<any>
42
+ opt:XOr(opt2: Option<any>): Option<any>
43
+ opt:Contains(value: any): boolean
44
+
45
+ --------------------------------------------------------------------
46
+
47
+ Options are useful for handling nil-value cases. Any time that an
48
+ operation might return nil, it is useful to instead return an
49
+ Option, which will indicate that the value might be nil, and should
50
+ be explicitly checked before using the value. This will help
51
+ prevent common bugs caused by nil values that can fail silently.
52
+
53
+
54
+ Example:
55
+
56
+ local result1 = Option.Some(32)
57
+ local result2 = Option.Some(nil)
58
+ local result3 = Option.Some("Hi")
59
+ local result4 = Option.Some(nil)
60
+ local result5 = Option.None
61
+
62
+ -- Use 'Match' to match if the value is Some or None:
63
+ result1:Match {
64
+ Some = function(value) print(value) end;
65
+ None = function() print("No value") end;
66
+ }
67
+
68
+ -- Raw check:
69
+ if result2:IsSome() then
70
+ local value = result2:Unwrap() -- Explicitly call Unwrap
71
+ print("Value of result2:", value)
72
+ end
73
+
74
+ if result3:IsNone() then
75
+ print("No result for result3")
76
+ end
77
+
78
+ -- Bad, will throw error bc result4 is none:
79
+ local value = result4:Unwrap()
80
+
81
+ --]]
82
+
83
+ export type MatchTable<T> = {
84
+ Some: (value: T) -> any,
85
+ None: () -> any,
86
+ }
87
+
88
+ export type MatchFn<T> = (matches: MatchTable<T>) -> any
89
+
90
+ export type DefaultFn<T> = () -> T
91
+
92
+ export type AndThenFn<T> = (value: T) -> Option<T>
93
+
94
+ export type OrElseFn<T> = () -> Option<T>
95
+
96
+ export type Option<T> = typeof(setmetatable(
97
+ {} :: {
98
+ Match: (self: Option<T>) -> MatchFn<T>,
99
+ IsSome: (self: Option<T>) -> boolean,
100
+ IsNone: (self: Option<T>) -> boolean,
101
+ Contains: (self: Option<T>, value: T) -> boolean,
102
+ Unwrap: (self: Option<T>) -> T,
103
+ Expect: (self: Option<T>, errMsg: string) -> T,
104
+ ExpectNone: (self: Option<T>, errMsg: string) -> nil,
105
+ UnwrapOr: (self: Option<T>, default: T) -> T,
106
+ UnwrapOrElse: (self: Option<T>, defaultFn: DefaultFn<T>) -> T,
107
+ And: (self: Option<T>, opt2: Option<T>) -> Option<T>,
108
+ AndThen: (self: Option<T>, predicate: AndThenFn<T>) -> Option<T>,
109
+ Or: (self: Option<T>, opt2: Option<T>) -> Option<T>,
110
+ OrElse: (self: Option<T>, orElseFunc: OrElseFn<T>) -> Option<T>,
111
+ XOr: (self: Option<T>, opt2: Option<T>) -> Option<T>,
112
+ },
113
+ {} :: {
114
+ __index: Option<T>,
115
+ }
116
+ ))
117
+
118
+ local CLASSNAME = "Option"
119
+
120
+ --[=[
121
+ @class Option
122
+
123
+ Represents an optional value in Lua. This is useful to avoid `nil` bugs, which can
124
+ go silently undetected within code and cause hidden or hard-to-find bugs.
125
+ ]=]
126
+ local Option = {}
127
+ Option.__index = Option
128
+
129
+ function Option._new(value)
130
+ local self = setmetatable({
131
+ ClassName = CLASSNAME,
132
+ _v = value,
133
+ _s = (value ~= nil),
134
+ }, Option)
135
+ return self
136
+ end
137
+
138
+ --[=[
139
+ @param value T
140
+ @return Option<T>
141
+
142
+ Creates an Option instance with the given value. Throws an error
143
+ if the given value is `nil`.
144
+ ]=]
145
+ function Option.Some(value)
146
+ assert(value ~= nil, "Option.Some() value cannot be nil")
147
+ return Option._new(value)
148
+ end
149
+
150
+ --[=[
151
+ @param value T
152
+ @return Option<T> | Option<None>
153
+
154
+ Safely wraps the given value as an option. If the
155
+ value is `nil`, returns `Option.None`, otherwise
156
+ returns `Option.Some(value)`.
157
+ ]=]
158
+ function Option.Wrap(value)
159
+ if value == nil then
160
+ return Option.None
161
+ else
162
+ return Option.Some(value)
163
+ end
164
+ end
165
+
166
+ --[=[
167
+ @param obj any
168
+ @return boolean
169
+ Returns `true` if `obj` is an Option.
170
+ ]=]
171
+ function Option.Is(obj)
172
+ return type(obj) == "table" and getmetatable(obj) == Option
173
+ end
174
+
175
+ --[=[
176
+ @param obj any
177
+ Throws an error if `obj` is not an Option.
178
+ ]=]
179
+ function Option.Assert(obj)
180
+ assert(Option.Is(obj), "Result was not of type Option")
181
+ end
182
+
183
+ --[=[
184
+ @param data table
185
+ @return Option
186
+ Deserializes the data into an Option. This data should have come from
187
+ the `option:Serialize()` method.
188
+ ]=]
189
+ function Option.Deserialize(data) -- type data = {ClassName: string, Value: any}
190
+ assert(type(data) == "table" and data.ClassName == CLASSNAME, "Invalid data for deserializing Option")
191
+ return data.Value == nil and Option.None or Option.Some(data.Value)
192
+ end
193
+
194
+ --[=[
195
+ @return table
196
+ Returns a serialized version of the option.
197
+ ]=]
198
+ function Option:Serialize()
199
+ return {
200
+ ClassName = self.ClassName,
201
+ Value = self._v,
202
+ }
203
+ end
204
+
205
+ --[=[
206
+ @param matches {Some: (value: any) -> any, None: () -> any}
207
+ @return any
208
+
209
+ Matches against the option.
210
+
211
+ ```lua
212
+ local opt = Option.Some(32)
213
+ opt:Match {
214
+ Some = function(num) print("Number", num) end,
215
+ None = function() print("No value") end,
216
+ }
217
+ ```
218
+ ]=]
219
+ function Option:Match(matches)
220
+ local onSome = matches.Some
221
+ local onNone = matches.None
222
+ assert(type(onSome) == "function", "Missing 'Some' match")
223
+ assert(type(onNone) == "function", "Missing 'None' match")
224
+ if self:IsSome() then
225
+ return onSome(self:Unwrap())
226
+ else
227
+ return onNone()
228
+ end
229
+ end
230
+
231
+ --[=[
232
+ @return boolean
233
+ Returns `true` if the option has a value.
234
+ ]=]
235
+ function Option:IsSome()
236
+ return self._s
237
+ end
238
+
239
+ --[=[
240
+ @return boolean
241
+ Returns `true` if the option is None.
242
+ ]=]
243
+ function Option:IsNone()
244
+ return not self._s
245
+ end
246
+
247
+ --[=[
248
+ @param msg string
249
+ @return value: any
250
+ Unwraps the value in the option, otherwise throws an error with `msg` as the error message.
251
+ ```lua
252
+ local opt = Option.Some(10)
253
+ print(opt:Expect("No number")) -> 10
254
+ print(Option.None:Expect("No number")) -- Throws an error "No number"
255
+ ```
256
+ ]=]
257
+ function Option:Expect(msg)
258
+ assert(self:IsSome(), msg)
259
+ return self._v
260
+ end
261
+
262
+ --[=[
263
+ @param msg string
264
+ Throws an error with `msg` as the error message if the value is _not_ None.
265
+ ]=]
266
+ function Option:ExpectNone(msg)
267
+ assert(self:IsNone(), msg)
268
+ end
269
+
270
+ --[=[
271
+ @return value: any
272
+ Returns the value in the option, or throws an error if the option is None.
273
+ ]=]
274
+ function Option:Unwrap()
275
+ return self:Expect("Cannot unwrap option of None type")
276
+ end
277
+
278
+ --[=[
279
+ @param default any
280
+ @return value: any
281
+ If the option holds a value, returns the value. Otherwise, returns `default`.
282
+ ]=]
283
+ function Option:UnwrapOr(default)
284
+ if self:IsSome() then
285
+ return self:Unwrap()
286
+ else
287
+ return default
288
+ end
289
+ end
290
+
291
+ --[=[
292
+ @param defaultFn () -> any
293
+ @return value: any
294
+ If the option holds a value, returns the value. Otherwise, returns the
295
+ result of the `defaultFn` function.
296
+ ]=]
297
+ function Option:UnwrapOrElse(defaultFn)
298
+ if self:IsSome() then
299
+ return self:Unwrap()
300
+ else
301
+ return defaultFn()
302
+ end
303
+ end
304
+
305
+ --[=[
306
+ @param optionB Option
307
+ @return Option
308
+ Returns `optionB` if the calling option has a value,
309
+ otherwise returns None.
310
+
311
+ ```lua
312
+ local optionA = Option.Some(32)
313
+ local optionB = Option.Some(64)
314
+ local opt = optionA:And(optionB)
315
+ -- opt == optionB
316
+
317
+ local optionA = Option.None
318
+ local optionB = Option.Some(64)
319
+ local opt = optionA:And(optionB)
320
+ -- opt == Option.None
321
+ ```
322
+ ]=]
323
+ function Option:And(optionB)
324
+ if self:IsSome() then
325
+ return optionB
326
+ else
327
+ return Option.None
328
+ end
329
+ end
330
+
331
+ --[=[
332
+ @param andThenFn (value: any) -> Option
333
+ @return value: Option
334
+ If the option holds a value, then the `andThenFn`
335
+ function is called with the held value of the option,
336
+ and then the resultant Option returned by the `andThenFn`
337
+ is returned. Otherwise, None is returned.
338
+
339
+ ```lua
340
+ local optA = Option.Some(32)
341
+ local optB = optA:AndThen(function(num)
342
+ return Option.Some(num * 2)
343
+ end)
344
+ print(optB:Expect("Expected number")) --> 64
345
+ ```
346
+ ]=]
347
+ function Option:AndThen(andThenFn)
348
+ if self:IsSome() then
349
+ local result = andThenFn(self:Unwrap())
350
+ Option.Assert(result)
351
+ return result
352
+ else
353
+ return Option.None
354
+ end
355
+ end
356
+
357
+ --[=[
358
+ @param optionB Option
359
+ @return Option
360
+ If caller has a value, returns itself. Otherwise, returns `optionB`.
361
+ ]=]
362
+ function Option:Or(optionB)
363
+ if self:IsSome() then
364
+ return self
365
+ else
366
+ return optionB
367
+ end
368
+ end
369
+
370
+ --[=[
371
+ @param orElseFn () -> Option
372
+ @return Option
373
+ If caller has a value, returns itself. Otherwise, returns the
374
+ option generated by the `orElseFn` function.
375
+ ]=]
376
+ function Option:OrElse(orElseFn)
377
+ if self:IsSome() then
378
+ return self
379
+ else
380
+ local result = orElseFn()
381
+ Option.Assert(result)
382
+ return result
383
+ end
384
+ end
385
+
386
+ --[=[
387
+ @param optionB Option
388
+ @return Option
389
+ If both `self` and `optionB` have values _or_ both don't have a value,
390
+ then this returns None. Otherwise, it returns the option that does have
391
+ a value.
392
+ ]=]
393
+ function Option:XOr(optionB)
394
+ local someOptA = self:IsSome()
395
+ local someOptB = optionB:IsSome()
396
+ if someOptA == someOptB then
397
+ return Option.None
398
+ elseif someOptA then
399
+ return self
400
+ else
401
+ return optionB
402
+ end
403
+ end
404
+
405
+ --[=[
406
+ @param predicate (value: any) -> boolean
407
+ @return Option
408
+ Returns `self` if this option has a value and the predicate returns `true.
409
+ Otherwise, returns None.
410
+ ]=]
411
+ function Option:Filter(predicate)
412
+ if self:IsNone() or not predicate(self._v) then
413
+ return Option.None
414
+ else
415
+ return self
416
+ end
417
+ end
418
+
419
+ --[=[
420
+ @param value any
421
+ @return boolean
422
+ Returns `true` if this option contains `value`.
423
+ ]=]
424
+ function Option:Contains(value)
425
+ return self:IsSome() and self._v == value
426
+ end
427
+
428
+ --[=[
429
+ @return string
430
+ Metamethod to transform the option into a string.
431
+ ```lua
432
+ local optA = Option.Some(64)
433
+ local optB = Option.None
434
+ print(optA) --> Option<number>
435
+ print(optB) --> Option<None>
436
+ ```
437
+ ]=]
438
+ function Option:__tostring()
439
+ if self:IsSome() then
440
+ return ("Option<" .. typeof(self._v) .. ">")
441
+ else
442
+ return "Option<None>"
443
+ end
444
+ end
445
+
446
+ --[=[
447
+ @return boolean
448
+ @param opt Option
449
+ Metamethod to check equality between two options. Returns `true` if both
450
+ options hold the same value _or_ both options are None.
451
+ ```lua
452
+ local o1 = Option.Some(32)
453
+ local o2 = Option.Some(32)
454
+ local o3 = Option.Some(64)
455
+ local o4 = Option.None
456
+ local o5 = Option.None
457
+
458
+ print(o1 == o2) --> true
459
+ print(o1 == o3) --> false
460
+ print(o1 == o4) --> false
461
+ print(o4 == o5) --> true
462
+ ```
463
+ ]=]
464
+ function Option:__eq(opt)
465
+ if Option.Is(opt) then
466
+ if self:IsSome() and opt:IsSome() then
467
+ return (self:Unwrap() == opt:Unwrap())
468
+ elseif self:IsNone() and opt:IsNone() then
469
+ return true
470
+ end
471
+ end
472
+ return false
473
+ end
474
+
475
+ --[=[
476
+ @prop None Option<None>
477
+ @within Option
478
+ Represents no value.
479
+ ]=]
480
+ Option.None = Option._new()
481
+
482
+ return (Option :: any) :: {
483
+ Some: <T>(value: T) -> Option<T>,
484
+ Wrap: <T>(value: T?) -> Option<T>,
485
+
486
+ Is: (obj: any) -> boolean,
487
+
488
+ None: Option<any>,
489
+ }