mirror of
https://github.com/CompeyDev/rusty-luau.git
synced 2024-12-12 04:40:40 +00:00
chore: remove bundled roblox scripts
This commit is contained in:
parent
edcef2015f
commit
35ada9759c
5 changed files with 0 additions and 2348 deletions
|
@ -1,210 +0,0 @@
|
||||||
--[[
|
|
||||||
This exists because if the Option module were to have methods to convert
|
|
||||||
to Result, and the Result module were to have methods to convert to Option,
|
|
||||||
there would be a cyclic dependency.
|
|
||||||
|
|
||||||
So, if a consumer of this library wants to convert between the two, they
|
|
||||||
would rather import this conversion module.
|
|
||||||
]]
|
|
||||||
|
|
||||||
local option = require(script.Parent.option)
|
|
||||||
local Option = option.Option
|
|
||||||
export type Option<T> = option.Option<T>
|
|
||||||
local None = option.None
|
|
||||||
local Some = option.Some
|
|
||||||
|
|
||||||
local result = require(script.Parent.result)
|
|
||||||
local Result = result.Result
|
|
||||||
export type Result<T, E> = result.Result<T, E>
|
|
||||||
local Ok = result.Ok
|
|
||||||
local Err = result.Err
|
|
||||||
|
|
||||||
--[=[
|
|
||||||
@within Result
|
|
||||||
|
|
||||||
Converts from [Result]`<T, E>` to [Option]`<T>`.
|
|
||||||
|
|
||||||
Converts `self` into an [Option]`<T>`, and discarding the error, if any.
|
|
||||||
|
|
||||||
```lua
|
|
||||||
local x: Result<number, string> = Ok(2)
|
|
||||||
assert(x:ok() == Some(2))
|
|
||||||
|
|
||||||
x = Err("Nothing here")
|
|
||||||
assert(x:ok() == None())
|
|
||||||
```
|
|
||||||
|
|
||||||
@param self Result<T, E>
|
|
||||||
@return Option<T>
|
|
||||||
]=]
|
|
||||||
function Result.ok<T, E>(self: Result<T, E>): Option<T>
|
|
||||||
if self:isOk() then
|
|
||||||
if self._value == nil then
|
|
||||||
return None()
|
|
||||||
end
|
|
||||||
|
|
||||||
return Some(self._value)
|
|
||||||
end
|
|
||||||
|
|
||||||
return None()
|
|
||||||
end
|
|
||||||
|
|
||||||
--[=[
|
|
||||||
@within Result
|
|
||||||
|
|
||||||
Converts from [Result]`<T, E>` to [Option]`<E>`.
|
|
||||||
|
|
||||||
Converts `self` into an [Option]`<E>`, and discarding the success value, if any.
|
|
||||||
|
|
||||||
```lua
|
|
||||||
local x: Result<number, string> = Ok(2)
|
|
||||||
assert(x:ok() == Some(2))
|
|
||||||
|
|
||||||
x = Err("Nothing here")
|
|
||||||
assert(x:ok() == None())
|
|
||||||
```
|
|
||||||
|
|
||||||
@param self Result<T, E>
|
|
||||||
@return Option<E>
|
|
||||||
]=]
|
|
||||||
function Result.err<T, E>(self: Result<T, E>): Option<E>
|
|
||||||
if self:isErr() then
|
|
||||||
return Option.new(self._error) :: Option<E>
|
|
||||||
end
|
|
||||||
|
|
||||||
return None()
|
|
||||||
end
|
|
||||||
|
|
||||||
--[=[
|
|
||||||
@within Result
|
|
||||||
|
|
||||||
Transposes a [Result] of an [Option] into an [Option] of a [Result].
|
|
||||||
|
|
||||||
[Result:Ok]\([Option:None]\) will be mapped to [Option:None].
|
|
||||||
[Result:Ok]\([Option:Some]`(_)`\) and [Result:Err]\(`_`\) will be mapped to
|
|
||||||
[Option:Some]\([Result:Ok]`(_)`\) and [Option:Some]\([Option:Err]`(_)`\).
|
|
||||||
|
|
||||||
```lua
|
|
||||||
type SomeErr = {}
|
|
||||||
|
|
||||||
local x: Result<Option<number>, SomeErr> = Ok(Some(2))
|
|
||||||
local y: Option<Result<number, SomeErr>> = Some(Ok(2))
|
|
||||||
assert(x:transpose() == y)
|
|
||||||
```
|
|
||||||
|
|
||||||
@param self Result<Option<T>, E>
|
|
||||||
@return Option<Result<T, E>>
|
|
||||||
]=]
|
|
||||||
function Result.transpose<T, E>(self: Result<Option<T>, E>): Option<Result<T, E>>
|
|
||||||
if self._value == None() then
|
|
||||||
return None()
|
|
||||||
elseif self:isOkAnd(function(val): boolean
|
|
||||||
return val._optValue == nil
|
|
||||||
end) then
|
|
||||||
return Some(Ok(self._value._optValue))
|
|
||||||
elseif self:isErr() then
|
|
||||||
return Some(Err(self._error))
|
|
||||||
end
|
|
||||||
|
|
||||||
error("`Result` is not transposable")
|
|
||||||
end
|
|
||||||
|
|
||||||
--[=[
|
|
||||||
@within Option
|
|
||||||
|
|
||||||
Transforms the [Option]`<T>` into a [Result]`<T, E>`, mapping [Option:Some]`(v)`
|
|
||||||
to [Result:Ok]`(v)` and [Option:None] to [Result:Err]`(err)`.
|
|
||||||
|
|
||||||
Arguments passed to [Option:okOr] are eagerly evaluated; if you are passing the result
|
|
||||||
of a function call, it is recommended to use [Option:okOrElse], which is lazily evaluated.
|
|
||||||
|
|
||||||
```lua
|
|
||||||
local x: Option<string> = Some("foo")
|
|
||||||
assert(x:okOr(0) == Ok("foo"))
|
|
||||||
|
|
||||||
x = None()
|
|
||||||
assert(x:okOr(0) == Err(0))
|
|
||||||
```
|
|
||||||
|
|
||||||
@param self Option<T>
|
|
||||||
@param err E
|
|
||||||
@return Result<T, E>
|
|
||||||
]=]
|
|
||||||
function Option.okOr<T, E>(self: Option<T>, err: E): Result<T, E>
|
|
||||||
if self:isSome() then
|
|
||||||
return Ok(self._optValue)
|
|
||||||
end
|
|
||||||
|
|
||||||
return Err(err)
|
|
||||||
end
|
|
||||||
|
|
||||||
--[=[
|
|
||||||
@within Option
|
|
||||||
|
|
||||||
Transforms the [Option]`<T>` into a [Result]`<T, E>`, mapping [Option:Some]`(v)` to
|
|
||||||
[Result:Ok]`(v)` and [Option:None] to [Result:Err]`(err())`.
|
|
||||||
|
|
||||||
```lua
|
|
||||||
local x: Option<string> = Some("foo")
|
|
||||||
assert(x:okOrElse(function() return 0 end) == Ok("foo"))
|
|
||||||
|
|
||||||
x = None()
|
|
||||||
assert(x:okOrElse(function() return 0 end) == Err(0))
|
|
||||||
```
|
|
||||||
|
|
||||||
@param self Option<T>
|
|
||||||
@param err () -> E
|
|
||||||
@return Result<T, E>
|
|
||||||
]=]
|
|
||||||
function Option.okOrElse<T, E>(self: Option<T>, err: () -> E): Result<T, E>
|
|
||||||
if self:isSome() then
|
|
||||||
return Ok(self._optValue :: T)
|
|
||||||
end
|
|
||||||
|
|
||||||
return Err(err())
|
|
||||||
end
|
|
||||||
|
|
||||||
--[=[
|
|
||||||
@within Option
|
|
||||||
|
|
||||||
Transposes a [Option] of an [Result] into an [Result] of a [Option].
|
|
||||||
|
|
||||||
[Option:None] will be mapped to [Result:Ok]\([Option:None]\).
|
|
||||||
[Option:Some]\([Result:Ok]`(_)`\) and [Option:Some]\([Result:Err]\(`_`\)\) will
|
|
||||||
be mapped to [Result:Ok]\([Option:Some]`(_)`\) and [Result:Err]`(_)`.
|
|
||||||
|
|
||||||
```lua
|
|
||||||
type SomeErr = {}
|
|
||||||
|
|
||||||
local x: Result<Option<number>, SomeErr> = Ok(Some(5))
|
|
||||||
local y: Option<Result<number, SomeErr>> = Some(Ok(5))
|
|
||||||
assert(x == y:transpose())
|
|
||||||
```
|
|
||||||
|
|
||||||
@param self Option<Result<T, E>>
|
|
||||||
@return Result<Option<T>, E>
|
|
||||||
]=]
|
|
||||||
function Option.transpose<T, E>(self: Option<Result<T, E>>): Result<Option<T>, E>
|
|
||||||
if self:isSome() then
|
|
||||||
local inner = self._optValue
|
|
||||||
assert(self.typeId == "Option" and inner.typeId == "Result", "Only an `Option` of a `Result` can be transposed")
|
|
||||||
|
|
||||||
if inner:isOk() then
|
|
||||||
return Some(Ok(inner._value))
|
|
||||||
elseif inner:isErr() then
|
|
||||||
return Some(Err(inner._error))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return Ok(None())
|
|
||||||
end
|
|
||||||
|
|
||||||
return {
|
|
||||||
Ok = Ok,
|
|
||||||
Err = Err,
|
|
||||||
Result = Result,
|
|
||||||
|
|
||||||
Some = Some,
|
|
||||||
None = None,
|
|
||||||
Option = Option,
|
|
||||||
}
|
|
218
rbx/future.luau
218
rbx/future.luau
|
@ -1,218 +0,0 @@
|
||||||
local isRoblox = _VERSION == "Luau"
|
|
||||||
local isLune = _VERSION:find("Lune") == 1
|
|
||||||
local deps = if isLune then require(script.Parent.Parent.deps)else require(script.Parent.deps)
|
|
||||||
local requires = deps(isRoblox, isLune)
|
|
||||||
local util = require(script.Parent.util)
|
|
||||||
|
|
||||||
local Signal = util.Signal
|
|
||||||
type Signal<T...> = util.Signal<T...>
|
|
||||||
|
|
||||||
local task = requires.task
|
|
||||||
|
|
||||||
local result = require(script.Parent.result)
|
|
||||||
type Result<T, E> = result.Result<T, E>
|
|
||||||
local Ok = result.Ok
|
|
||||||
local Err = result.Err
|
|
||||||
|
|
||||||
local option = require(script.Parent.option)
|
|
||||||
type Option<T> = option.Option<T>
|
|
||||||
local None = option.None
|
|
||||||
local Some = option.Some
|
|
||||||
|
|
||||||
--[=[
|
|
||||||
@class Future
|
|
||||||
|
|
||||||
A future represents an asynchronous computation.
|
|
||||||
|
|
||||||
A future is a value that might not have finished computing yet. This kind of “asynchronous value”
|
|
||||||
makes it possible for a thread to continue doing useful work while it waits for the value to
|
|
||||||
become available.
|
|
||||||
|
|
||||||
### The [Future:poll] Method
|
|
||||||
The core method of future, poll, attempts to resolve the future into a final value. This method does
|
|
||||||
not block if the value is not ready. Instead, the current task is executed in the background, and
|
|
||||||
its progress is reported when polled. When using a future, you generally won’t call poll directly,
|
|
||||||
but instead [Future:await] the value.
|
|
||||||
|
|
||||||
```lua
|
|
||||||
local net = require("@lune/net")
|
|
||||||
|
|
||||||
local fut: Future<Result<string, string>> = Future.try(function(url)
|
|
||||||
local resp = net.request({
|
|
||||||
url = url,
|
|
||||||
method = "GET",
|
|
||||||
})
|
|
||||||
|
|
||||||
assert(resp.ok)
|
|
||||||
|
|
||||||
return resp.body
|
|
||||||
end, { "https://jsonplaceholder.typicode.com/posts/1" })
|
|
||||||
|
|
||||||
local resp: Result<string, string> = fut:await()
|
|
||||||
print(net.jsonDecode(resp:unwrap()))
|
|
||||||
```
|
|
||||||
]=]
|
|
||||||
local Future = {}
|
|
||||||
|
|
||||||
--[=[
|
|
||||||
@private
|
|
||||||
@type Status "initialized" | "pending" | "cancelled" | "ready"
|
|
||||||
@within Future
|
|
||||||
|
|
||||||
Represents the status of a [Future].
|
|
||||||
]=]
|
|
||||||
export type Status = "initialized" | "pending" | "cancelled" | "ready"
|
|
||||||
|
|
||||||
--[=[
|
|
||||||
@private
|
|
||||||
@interface Future<T>
|
|
||||||
@within Future
|
|
||||||
|
|
||||||
Represents the internal state of a [Future].
|
|
||||||
|
|
||||||
@field _thread thread -- The background coroutine spawned for execution
|
|
||||||
@field _ret T -- The value returned once execution has halted
|
|
||||||
@field _spawnEvt Signal<()> -- Event for internal communication among threads pre execution
|
|
||||||
@field _retEvt Signal<T | Result<T, string>, Status> -- Event for internal communication among threads post execution
|
|
||||||
@field _status Status -- The status of the Future
|
|
||||||
|
|
||||||
]=]
|
|
||||||
export type Future<T> = typeof(Future) & {
|
|
||||||
_ret: T,
|
|
||||||
_thread: thread,
|
|
||||||
_spawnEvt: Signal<()>,
|
|
||||||
_retEvt: Signal<T | Result<T, string>, Status>,
|
|
||||||
_status: Status
|
|
||||||
}
|
|
||||||
|
|
||||||
local function _constructor<T>(fn: (Signal<()>, Signal<T, Status>) -> ())
|
|
||||||
return setmetatable(
|
|
||||||
{
|
|
||||||
_thread = coroutine.create(fn),
|
|
||||||
|
|
||||||
_spawnEvt = Signal.new(),
|
|
||||||
-- This is a hack to make luau realize that this object and
|
|
||||||
-- Future<T> are related
|
|
||||||
_retEvt = Signal.new() :: Signal<T | Result<T, string>, Status>,
|
|
||||||
|
|
||||||
_status = "initialized",
|
|
||||||
} :: Future<T>,
|
|
||||||
{
|
|
||||||
__index = Future,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
--[=[
|
|
||||||
@within Future
|
|
||||||
|
|
||||||
Constructs a [Future] from a function to be run asynchronously.
|
|
||||||
|
|
||||||
:::caution
|
|
||||||
If a the provided function has the possibility to throw an error, instead of any
|
|
||||||
other rusty-luau types like [Result] or [Option], use [Future:try] instead.
|
|
||||||
:::
|
|
||||||
|
|
||||||
@param fn -- The function to be executed asynchronously
|
|
||||||
@param args -- The arguments table to be passed to to the function
|
|
||||||
@return Future<T> -- The constructed future
|
|
||||||
]=]
|
|
||||||
function Future.new<T>(fn: (...any) -> T, args: { any })
|
|
||||||
return _constructor(function(spawnEvt: Signal<()>, retEvt: Signal<T, Status>)
|
|
||||||
spawnEvt:Fire()
|
|
||||||
|
|
||||||
local ret = fn(table.unpack(args))
|
|
||||||
retEvt:Fire(ret, "ready")
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
--[=[
|
|
||||||
@within Future
|
|
||||||
|
|
||||||
Constructs a fallible [Future] from a function to be run asynchronously.
|
|
||||||
|
|
||||||
@param fn -- The fallible function to be executed asynchronously
|
|
||||||
@param args -- The arguments table to be passed to to the function
|
|
||||||
@return Future<Result<T>> -- The constructed future
|
|
||||||
]=]
|
|
||||||
function Future.try<T>(fn: (...any) -> T, args: { any })
|
|
||||||
return _constructor(function(spawnEvt: Signal<()>, retEvt: Signal<Result<T, string>, Status>)
|
|
||||||
spawnEvt:Fire()
|
|
||||||
|
|
||||||
local ok, ret = pcall(fn, table.unpack(args))
|
|
||||||
local res: Result<T, string> = if ok then Ok(ret) else Err(ret)
|
|
||||||
retEvt:Fire(res, "ready")
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
--[=[
|
|
||||||
@within Future
|
|
||||||
|
|
||||||
Polls a [Future] to completion.
|
|
||||||
|
|
||||||
@param self Future<T>
|
|
||||||
@return (Status, Option<T>) -- Returns the [Status] and an optional return if completed
|
|
||||||
]=]
|
|
||||||
function Future.poll<T>(self: Future<T>): (Status, Option<T>)
|
|
||||||
if self._status == "initialized" then
|
|
||||||
self._retEvt:Connect(function(firedRet, status: Status)
|
|
||||||
self._status = status
|
|
||||||
self._ret = firedRet
|
|
||||||
|
|
||||||
-- Cleanup
|
|
||||||
coroutine.yield(self._thread)
|
|
||||||
coroutine.close(self._thread)
|
|
||||||
|
|
||||||
self._spawnEvt:DisconnectAll()
|
|
||||||
self._retEvt:DisconnectAll()
|
|
||||||
end)
|
|
||||||
|
|
||||||
self._spawnEvt:Connect(function()
|
|
||||||
self._status = "pending"
|
|
||||||
end)
|
|
||||||
|
|
||||||
coroutine.resume(self._thread, self._spawnEvt, self._retEvt)
|
|
||||||
end
|
|
||||||
|
|
||||||
if self._status == "pending" then
|
|
||||||
-- Just wait a bit more for the signal to fire
|
|
||||||
task.wait(0.01)
|
|
||||||
end
|
|
||||||
|
|
||||||
local retOpt = if self._ret == nil then None() else Some(self._ret)
|
|
||||||
return self._status, retOpt
|
|
||||||
end
|
|
||||||
|
|
||||||
--[=[
|
|
||||||
@within Future
|
|
||||||
|
|
||||||
Cancels a [Future].
|
|
||||||
|
|
||||||
@param self Future<T>
|
|
||||||
]=]
|
|
||||||
function Future.cancel<T>(self: Future<T>)
|
|
||||||
self._retEvt:Fire(nil :: any, "cancelled")
|
|
||||||
self._status = "cancelled"
|
|
||||||
end
|
|
||||||
|
|
||||||
--[=[
|
|
||||||
@within Future
|
|
||||||
|
|
||||||
Suspend execution until the result of a [Future] is ready.
|
|
||||||
This method continuosly polls a [Future] until it reaches completion.
|
|
||||||
|
|
||||||
@param self Future<T>
|
|
||||||
@return T -- The value returned by the function on completion
|
|
||||||
]=]
|
|
||||||
function Future.await<T>(self: Future<T>): Option<T>
|
|
||||||
while true do
|
|
||||||
local status: Status, ret: Option<T> = self:poll()
|
|
||||||
|
|
||||||
if status == "ready" then
|
|
||||||
-- Safe to unwrap, we know it must not be nil
|
|
||||||
return ret
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return Future
|
|
918
rbx/option.luau
918
rbx/option.luau
|
@ -1,918 +0,0 @@
|
||||||
local tableEq = require(script.Parent.util).tableEq
|
|
||||||
|
|
||||||
--[=[
|
|
||||||
@class Option
|
|
||||||
|
|
||||||
Type [Option] represents an optional value: every [Option] is either [Option:Some]
|
|
||||||
and contains a value, or [Option:None], and does not. Common uses of an [Option]
|
|
||||||
may involve:
|
|
||||||
|
|
||||||
* Initial values
|
|
||||||
* Return values for functions that are not defined over their entire input range (partial functions)
|
|
||||||
* Return value for otherwise reporting simple errors, where None is returned on error
|
|
||||||
* Optional object fields
|
|
||||||
* Values that can be loaned or “taken”
|
|
||||||
* Optional function arguments
|
|
||||||
|
|
||||||
```lua
|
|
||||||
function divide(numerator: number, denominator: number): Option<number>
|
|
||||||
if denominator == 0 then
|
|
||||||
None()
|
|
||||||
else
|
|
||||||
return Some(numerator / denominator)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
```
|
|
||||||
]=]
|
|
||||||
local Option = {}
|
|
||||||
|
|
||||||
export type Option<T> = typeof(Option) & {
|
|
||||||
_optValue: T?,
|
|
||||||
typeId: "Option"
|
|
||||||
}
|
|
||||||
|
|
||||||
--[=[
|
|
||||||
@within Option
|
|
||||||
|
|
||||||
No value.
|
|
||||||
]=]
|
|
||||||
function None<T>(): Option<T>
|
|
||||||
return Option.new(nil) :: Option<T>
|
|
||||||
end
|
|
||||||
|
|
||||||
--[=[
|
|
||||||
@within Option
|
|
||||||
|
|
||||||
Some value of type `T`.
|
|
||||||
]=]
|
|
||||||
function Some<T>(val: T): Option<T>
|
|
||||||
return Option.new(val) :: Option<T>
|
|
||||||
end
|
|
||||||
|
|
||||||
--[=[
|
|
||||||
@within Option
|
|
||||||
|
|
||||||
Converts a potentially `nil` value into an [Option].
|
|
||||||
|
|
||||||
@param val T? -- The value to convert into an [Option]
|
|
||||||
@return Option<T>
|
|
||||||
]=]
|
|
||||||
function Option.new<T>(val: T?)
|
|
||||||
return setmetatable(
|
|
||||||
{
|
|
||||||
_optValue = val,
|
|
||||||
typeId = "Option",
|
|
||||||
} :: Option<T>,
|
|
||||||
{
|
|
||||||
__index = Option,
|
|
||||||
__tostring = function<T>(self: Option<T>)
|
|
||||||
if self._optValue == nil then
|
|
||||||
return `{self.typeId}::None`
|
|
||||||
else
|
|
||||||
return `{self.typeId}::Some({self._optValue})`
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
|
|
||||||
__eq = function<T>(self: Option<T>, other: Option<T>): boolean
|
|
||||||
if typeof(self._optValue) == "table" and typeof(other._optValue) == "table" then
|
|
||||||
return tableEq(self._optValue, other._optValue)
|
|
||||||
else
|
|
||||||
return self._optValue == other._optValue
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
__lt = function<T>(self: Option<T>, other: Option<T>): boolean
|
|
||||||
if self:isSome() and other:isSome() then
|
|
||||||
return (self._optValue :: any) < (other._optValue :: any)
|
|
||||||
end
|
|
||||||
|
|
||||||
return false
|
|
||||||
end,
|
|
||||||
__le = function<T>(self: Option<T>, other: Option<T>): boolean
|
|
||||||
if self:isSome() and other:isSome() then
|
|
||||||
return (self._optValue :: any) <= (other._optValue :: any)
|
|
||||||
end
|
|
||||||
|
|
||||||
return false
|
|
||||||
end,
|
|
||||||
}
|
|
||||||
|
|
||||||
-- TODO: Implement __iter, once iterators traits exist
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
--[=[
|
|
||||||
@within Option
|
|
||||||
|
|
||||||
Returns `true` is the [Option] is an [Option:Some] value.
|
|
||||||
|
|
||||||
@param self Option<T>
|
|
||||||
@return boolean
|
|
||||||
]=]
|
|
||||||
function Option.isSome<T>(self: Option<T>): boolean
|
|
||||||
return self._optValue ~= nil
|
|
||||||
end
|
|
||||||
|
|
||||||
--[=[
|
|
||||||
@within Option
|
|
||||||
|
|
||||||
Returns `true` is the [Option] is an [Option:Some] value and the value inside of it
|
|
||||||
matches a predicate.
|
|
||||||
|
|
||||||
@param self Option<T>
|
|
||||||
@param op (val: T) -> boolean -- The predicate function
|
|
||||||
@return boolean
|
|
||||||
]=]
|
|
||||||
function Option.isSomeAnd<T>(self: Option<T>, op: (val: T) -> boolean): boolean
|
|
||||||
-- We know that it's not None, so this is fine
|
|
||||||
return self:isSome() and op(self._optValue :: T)
|
|
||||||
end
|
|
||||||
|
|
||||||
--[=[
|
|
||||||
@within Option
|
|
||||||
|
|
||||||
Returns `true` is the [Option] is an [Option:None] value.
|
|
||||||
|
|
||||||
@param self Option<T>
|
|
||||||
@return boolean
|
|
||||||
]=]
|
|
||||||
function Option.isNone<T>(self: Option<T>): boolean
|
|
||||||
return not self:isSome()
|
|
||||||
end
|
|
||||||
|
|
||||||
--[=[
|
|
||||||
@within Option
|
|
||||||
|
|
||||||
Returns the [Option:Some].
|
|
||||||
|
|
||||||
@error panic -- Panics if there is an [Option:None] with a custom panic message provided by `msg`.
|
|
||||||
|
|
||||||
```lua
|
|
||||||
local x: Option<string> = Some("value")
|
|
||||||
assert(x:expect("fruits are healthy") == "value")
|
|
||||||
```
|
|
||||||
|
|
||||||
```lua
|
|
||||||
local x: Option<string> = None()
|
|
||||||
x:expect("fruits are healthy") -- panics with `fruits are healthy`
|
|
||||||
```
|
|
||||||
|
|
||||||
@param self Option<T>
|
|
||||||
@param msg string -- The panic message
|
|
||||||
@return boolean
|
|
||||||
]=]
|
|
||||||
function Option.expect<T>(self: Option<T>, msg: string): T | never
|
|
||||||
if self:isSome() then
|
|
||||||
return self._optValue :: T
|
|
||||||
end
|
|
||||||
|
|
||||||
return error(`panic: {msg}; expected Option to be type Option::Some(T), got Option::None`)
|
|
||||||
end
|
|
||||||
|
|
||||||
--[=[
|
|
||||||
@within Option
|
|
||||||
|
|
||||||
Returns the [Option:Some].
|
|
||||||
|
|
||||||
Because this function may panic, its use is generally discouraged. Instead, prefer to use
|
|
||||||
[Option:unwrapOr], [Option:unwrapOrElse], or [Option:unwrapOrDefault].
|
|
||||||
|
|
||||||
@error panic -- Panics if the self value is [Option:None].
|
|
||||||
@param self Option<T>
|
|
||||||
@return T | never
|
|
||||||
]=]
|
|
||||||
function Option.unwrap<T>(self: Option<T>): T | never
|
|
||||||
if self:isSome() then
|
|
||||||
return self._optValue :: T
|
|
||||||
end
|
|
||||||
|
|
||||||
return error("called `Option:unwrap()` on a `None` value")
|
|
||||||
end
|
|
||||||
|
|
||||||
--[=[
|
|
||||||
@within Option
|
|
||||||
|
|
||||||
Returns the contained [Option:Some] value or a provided default.
|
|
||||||
|
|
||||||
Arguments passed to unwrap_or are eagerly evaluated; if you are passing the result of
|
|
||||||
a function call, it is recommended to use [Option:unwrapOrElse], which is lazily
|
|
||||||
evaluated.
|
|
||||||
|
|
||||||
```lua
|
|
||||||
assert(Some("car"):unwrapOr("bike") == "car")
|
|
||||||
assert(None():unwrapOr("bike") == "bike")
|
|
||||||
```
|
|
||||||
|
|
||||||
@param self Option<T>
|
|
||||||
@param default T -- The default value
|
|
||||||
@return T
|
|
||||||
]=]
|
|
||||||
function Option.unwrapOr<T>(self: Option<T>, default: T): T
|
|
||||||
if self:isSome() then
|
|
||||||
return self._optValue :: T
|
|
||||||
end
|
|
||||||
|
|
||||||
return default
|
|
||||||
end
|
|
||||||
|
|
||||||
--[=[
|
|
||||||
@within Option
|
|
||||||
|
|
||||||
Returns the contained [Option:Some] value or computes it from a function.
|
|
||||||
|
|
||||||
```lua
|
|
||||||
local k = 10
|
|
||||||
assert(Some(4).unwrapOrElse(function()
|
|
||||||
return 2 * k
|
|
||||||
end) == 4)
|
|
||||||
assert(None().unwrapOrElse(function()
|
|
||||||
return 2 * k
|
|
||||||
end) == 20)
|
|
||||||
```
|
|
||||||
|
|
||||||
@param self Option<T>
|
|
||||||
@param default () -> T -- The function which computes the default value
|
|
||||||
@return T
|
|
||||||
]=]
|
|
||||||
function Option.unwrapOrElse<T>(self: Option<T>, default: () -> T): T
|
|
||||||
if self:isSome() then
|
|
||||||
return self._optValue :: T
|
|
||||||
end
|
|
||||||
|
|
||||||
return default()
|
|
||||||
end
|
|
||||||
|
|
||||||
--[=[
|
|
||||||
@within Option
|
|
||||||
|
|
||||||
Maps an [Option]<T> to [Option]<U> by applying a function to a contained value
|
|
||||||
(if [Option:Some]) or returns [Option:None](if [Option:None]).
|
|
||||||
|
|
||||||
```lua
|
|
||||||
local maybeSomeString: Option<string> = Some("Hello, World!")
|
|
||||||
|
|
||||||
local maybeSomeLen: Option<number> = maybeSomeString:map(function(s)
|
|
||||||
return #s
|
|
||||||
end)
|
|
||||||
assert(maybeSomeLen == 13)
|
|
||||||
|
|
||||||
local x: Option<string> = None()
|
|
||||||
assert(x:map(function(s)
|
|
||||||
return #s
|
|
||||||
end) == None())
|
|
||||||
```
|
|
||||||
|
|
||||||
@param self Option<T>
|
|
||||||
@param op (x: T) -> U? -- The function to apply
|
|
||||||
@return Option<U>
|
|
||||||
]=]
|
|
||||||
function Option.map<T, U>(self: Option<T>, op: (x: T) -> U?): Option<U>
|
|
||||||
if self:isSome() then
|
|
||||||
local val = op(self._optValue :: T)
|
|
||||||
|
|
||||||
if val == nil then
|
|
||||||
return None()
|
|
||||||
end
|
|
||||||
|
|
||||||
return Some(val)
|
|
||||||
end
|
|
||||||
|
|
||||||
return None()
|
|
||||||
end
|
|
||||||
|
|
||||||
--[=[
|
|
||||||
@within Option
|
|
||||||
|
|
||||||
Calls the provided closure with the contained value (if [Option:Some]).
|
|
||||||
|
|
||||||
```lua
|
|
||||||
local v = { 1, 2, 3, 4, 5 }
|
|
||||||
|
|
||||||
-- prints "got: 4"
|
|
||||||
local x: Option<number> = Option.new(v[4]):inspect(function(x)
|
|
||||||
print("got: " .. x)
|
|
||||||
end)
|
|
||||||
|
|
||||||
-- prints nothing
|
|
||||||
local x: Option<number> = Option.new(v[5]):inspect(function(x)
|
|
||||||
print("got: " .. x)
|
|
||||||
end)
|
|
||||||
```
|
|
||||||
|
|
||||||
@param self Option<T>
|
|
||||||
@param op (x: T) -> () -- The function to call
|
|
||||||
@return Option<T>
|
|
||||||
]=]
|
|
||||||
function Option.inspect<T>(self: Option<T>, op: (x: T) -> ()): Option<T>
|
|
||||||
if self:isSome() then
|
|
||||||
op(self._optValue :: T)
|
|
||||||
end
|
|
||||||
|
|
||||||
return self
|
|
||||||
end
|
|
||||||
|
|
||||||
--[=[
|
|
||||||
@within Option
|
|
||||||
|
|
||||||
Returns the provided default result (if none), or applies a function to
|
|
||||||
the contained value (if any).
|
|
||||||
|
|
||||||
Arguments passed to [Option:mapOr] are eagerly evaluated; if you are passing the
|
|
||||||
result of a function call, it is recommended to use [Option:mapOrElse],
|
|
||||||
which is lazily evaluated.
|
|
||||||
|
|
||||||
```lua
|
|
||||||
local x: Option<string> = Some("foo")
|
|
||||||
assert(x.mapOr(42, function(v) return #v end) == 3)
|
|
||||||
|
|
||||||
local x: Option<string> = None()
|
|
||||||
assert(x.mapOr(42, function(v) return #v end) == 42)
|
|
||||||
```
|
|
||||||
@param self Option<T>
|
|
||||||
@param default U -- The default value
|
|
||||||
@param op (val: T) -> U -- The function to apply
|
|
||||||
@return Option<T>
|
|
||||||
]=]
|
|
||||||
function Option.mapOr<T, U>(self: Option<T>, default: U, op: (val: T) -> U)
|
|
||||||
if self:isSome() then
|
|
||||||
return op(self._optValue :: T)
|
|
||||||
end
|
|
||||||
|
|
||||||
return default
|
|
||||||
end
|
|
||||||
|
|
||||||
--[=[
|
|
||||||
@within Option
|
|
||||||
|
|
||||||
Computes a default function result (if none), or applies a different function
|
|
||||||
to the contained value (if any).
|
|
||||||
|
|
||||||
```lua
|
|
||||||
local k = 21;
|
|
||||||
|
|
||||||
local x: Option<string> = Some("foo")
|
|
||||||
assert(
|
|
||||||
x:mapOrElse(
|
|
||||||
function() return 2 * k end,
|
|
||||||
function(v) return #v end
|
|
||||||
) == 3
|
|
||||||
)
|
|
||||||
|
|
||||||
local x: Option<string> = None()
|
|
||||||
assert(
|
|
||||||
x:mapOrElse(
|
|
||||||
function() return 2 * k end,
|
|
||||||
function(v) return #v end
|
|
||||||
) == 42
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
@param self Option<T>
|
|
||||||
@param default () -> U -- The function to compute the default value
|
|
||||||
@param op (val: T) -> U -- The function to apply
|
|
||||||
@return Option<T>
|
|
||||||
]=]
|
|
||||||
function Option.mapOrElse<T, U>(self: Option<T>, default: () -> U, op: (val: T) -> U): U
|
|
||||||
if self:isSome() then
|
|
||||||
return op(self._optValue :: T)
|
|
||||||
end
|
|
||||||
|
|
||||||
return default()
|
|
||||||
end
|
|
||||||
|
|
||||||
-- TODO: Iterator traits
|
|
||||||
function Option.iter(): never
|
|
||||||
return error("Unimplemented: `Option:iter()`")
|
|
||||||
end
|
|
||||||
|
|
||||||
--[=[
|
|
||||||
@within Option
|
|
||||||
|
|
||||||
Returns [Option:None] if the option is [Option:None], otherwise returns `optb`.
|
|
||||||
|
|
||||||
Arguments passed to and are eagerly evaluated; if you are passing the result of a
|
|
||||||
function call, it is recommended to use [Option:andThen], which is lazily evaluated.
|
|
||||||
|
|
||||||
```lua
|
|
||||||
local x: Option<number> = Some(2)
|
|
||||||
local y: Option<String> = None()
|
|
||||||
assert(x:and_(y) == None())
|
|
||||||
|
|
||||||
local x: Option<number> = None()
|
|
||||||
local y: Option<string> = Some("foo")
|
|
||||||
assert(x:and_(y) == None())
|
|
||||||
|
|
||||||
local x: Option<number> = Some(2)
|
|
||||||
local y: Option<string> = Some("foo")
|
|
||||||
assert(x:and_(y) == Some("foo"))
|
|
||||||
|
|
||||||
local x: Option<u32> = None()
|
|
||||||
local y: Option<string> = None()
|
|
||||||
assert(x:and_(y), None())
|
|
||||||
```
|
|
||||||
|
|
||||||
@param self Option<T>
|
|
||||||
@param optb Option<U> -- The other option
|
|
||||||
@return Option<U>
|
|
||||||
]=]
|
|
||||||
function Option.and_<T, U>(self: Option<T>, optb: Option<U>): Option<U>
|
|
||||||
if self:isSome() then
|
|
||||||
return optb
|
|
||||||
end
|
|
||||||
|
|
||||||
return None()
|
|
||||||
end
|
|
||||||
|
|
||||||
--[=[
|
|
||||||
@within Option
|
|
||||||
|
|
||||||
Returns [Option:None] if the option is [Option:None], otherwise calls `op` with the wrapped
|
|
||||||
value and returns the result.
|
|
||||||
|
|
||||||
Some languages call this operation flatmap.
|
|
||||||
|
|
||||||
```lua
|
|
||||||
function sqThenToString(x: number): Option<string>
|
|
||||||
return Option.new(x ^ 2):map(function(sq)
|
|
||||||
return tostring(sq)
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
assert(Some(2):andThen(sqThenToString) == Some(tostring(4)))
|
|
||||||
assert(None():andThen(sqThenToString) == None())
|
|
||||||
```
|
|
||||||
|
|
||||||
Often used to chain fallible operations that may return [Option:None].
|
|
||||||
|
|
||||||
```lua
|
|
||||||
local arr2d = { { "A0", "A1" }, { "B0", "B1" } }
|
|
||||||
|
|
||||||
local item01: Option<string> = Option.new(arr2d[1]):andThen(function(row)
|
|
||||||
return row[2]
|
|
||||||
end)
|
|
||||||
assert(item01 == Some("A1"))
|
|
||||||
|
|
||||||
local item20: Option<string> = Option.new(arr2d[3]):andThen(function(row)
|
|
||||||
return row[0]
|
|
||||||
end)
|
|
||||||
assert(item20 == None())
|
|
||||||
```
|
|
||||||
|
|
||||||
@param self Option<T>
|
|
||||||
@param op (val: T) -> Option<U> -- The function to call
|
|
||||||
@return Option<U>
|
|
||||||
]=]
|
|
||||||
function Option.andThen<T, U>(self: Option<T>, op: (val: T) -> Option<U>): Option<U>
|
|
||||||
if self:isSome() then
|
|
||||||
return op(self._optValue :: T)
|
|
||||||
end
|
|
||||||
|
|
||||||
return None()
|
|
||||||
end
|
|
||||||
|
|
||||||
--[=[
|
|
||||||
@within Option
|
|
||||||
|
|
||||||
Returns [Option:None] if the option is [Option:None], otherwise calls `predicate` with
|
|
||||||
the wrapped value and returns:
|
|
||||||
|
|
||||||
* [Option:Some](t) if predicate returns `true` (where `t` is the wrapped value), and
|
|
||||||
* [Option:None] if predicate returns `false`.
|
|
||||||
|
|
||||||
```lua
|
|
||||||
function isEven(n: number): boolean
|
|
||||||
return n % 2 == 0
|
|
||||||
end
|
|
||||||
|
|
||||||
assert(None():filter(isEven) == None())
|
|
||||||
assert(Some(3):filter(isEven) == None())
|
|
||||||
assert(Some(4):filter(isEven) == Some(4))
|
|
||||||
```
|
|
||||||
|
|
||||||
@param self Option<T>
|
|
||||||
@param predicate (val: T) -> boolean -- The predicate function which must match an element
|
|
||||||
@return Option<T>
|
|
||||||
]=]
|
|
||||||
function Option.filter<T>(self: Option<T>, predicate: (val: T) -> boolean): Option<T>
|
|
||||||
if self:isSome() then
|
|
||||||
if predicate(self._optValue :: T) then
|
|
||||||
return self
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return None()
|
|
||||||
end
|
|
||||||
|
|
||||||
--[=[
|
|
||||||
@within Option
|
|
||||||
|
|
||||||
Returns the option if it contains a value, otherwise returns `optb`.
|
|
||||||
|
|
||||||
Arguments passed to [Option:or_] are eagerly evaluated; if you are passing the result of
|
|
||||||
a function call, it is recommended to use [Option:orElse], which is lazily
|
|
||||||
evaluated.
|
|
||||||
|
|
||||||
```lua
|
|
||||||
local x: Option<number> = Some(2)
|
|
||||||
local y: Option<number> = None()
|
|
||||||
assert(x:or_(y) == Some(2))
|
|
||||||
|
|
||||||
local x: Option<number> = None()
|
|
||||||
local y: Option<number> = Some(100)
|
|
||||||
assert(x:or_(y) == Some(100))
|
|
||||||
|
|
||||||
local x: Option<number> = Some(2)
|
|
||||||
local y: Option<number> = Some(100)
|
|
||||||
assert(x:or_(y) == Some(2))
|
|
||||||
|
|
||||||
local x: Option<number> = None()
|
|
||||||
local y: Option<number> = None()
|
|
||||||
assert(x:or_(y), None())
|
|
||||||
```
|
|
||||||
|
|
||||||
@param self Option<T>
|
|
||||||
@param optb Option<T> -- The other option
|
|
||||||
@return Option<T>
|
|
||||||
]=]
|
|
||||||
function Option.or_<T>(self: Option<T>, optb: Option<T>): Option<T>
|
|
||||||
if self:isSome() then
|
|
||||||
return self
|
|
||||||
end
|
|
||||||
|
|
||||||
return optb
|
|
||||||
end
|
|
||||||
|
|
||||||
--[=[
|
|
||||||
@within Option
|
|
||||||
|
|
||||||
Returns the option if it contains a value, otherwise calls `op` and returns the result.
|
|
||||||
|
|
||||||
```lua
|
|
||||||
function nobody(): Option<string>
|
|
||||||
return None()
|
|
||||||
end
|
|
||||||
function vikings(): Option<string>
|
|
||||||
return Some("vikings")
|
|
||||||
end
|
|
||||||
|
|
||||||
assert(Some("barbarians"):orElse(vikings) == Some("barbarians"))
|
|
||||||
assert(None():orElse(vikings) == Some("vikings"))
|
|
||||||
assert(None():orElse(nobody) == None())
|
|
||||||
```
|
|
||||||
|
|
||||||
@param self Option<T>
|
|
||||||
@param op () -> Option<T> -- The function to call
|
|
||||||
@return Option<T>
|
|
||||||
]=]
|
|
||||||
function Option.orElse<T>(self: Option<T>, op: () -> Option<T>): Option<T>
|
|
||||||
if self:isSome() then
|
|
||||||
return self
|
|
||||||
end
|
|
||||||
|
|
||||||
return op()
|
|
||||||
end
|
|
||||||
|
|
||||||
--[=[
|
|
||||||
@within Option
|
|
||||||
|
|
||||||
Returns [Option:Some] if exactly one of `self`, `optb` is [Option:Some],
|
|
||||||
otherwise returns [Option:None].
|
|
||||||
|
|
||||||
```lua
|
|
||||||
local x: Option<number> = Some(2)
|
|
||||||
local y: Option<number> = None()
|
|
||||||
assert(x:xor(y) == Some(2))
|
|
||||||
|
|
||||||
local x: Option<number> = None()
|
|
||||||
local y: Option<number> = Some(2)
|
|
||||||
assert(x:xor(y) == Some(2))
|
|
||||||
|
|
||||||
local x: Option<number> = Some(2)
|
|
||||||
local y: Option<number> = Some(2)
|
|
||||||
assert(x:xor(y) == None())
|
|
||||||
|
|
||||||
local x: Option<number> = None()
|
|
||||||
local y: Option<number> = None()
|
|
||||||
assert(x:xor(y) == None())
|
|
||||||
```
|
|
||||||
|
|
||||||
@param self Option<T>
|
|
||||||
@param optb Option<T> -- The other option
|
|
||||||
@return Option<T>
|
|
||||||
]=]
|
|
||||||
function Option.xor<T>(self: Option<T>, optb: Option<T>): Option<T>
|
|
||||||
if self:isSome() and optb:isSome() then
|
|
||||||
return None()
|
|
||||||
end
|
|
||||||
|
|
||||||
if self:isSome() then
|
|
||||||
return self
|
|
||||||
elseif optb:isSome() then
|
|
||||||
return optb
|
|
||||||
end
|
|
||||||
|
|
||||||
return None()
|
|
||||||
end
|
|
||||||
|
|
||||||
--[=[
|
|
||||||
@within Option
|
|
||||||
|
|
||||||
Inserts value into the option, then returns it.
|
|
||||||
|
|
||||||
If the option already contains a value, the old value is dropped.
|
|
||||||
|
|
||||||
See also [Option:getOrInsert], which doesn’t update the value if the
|
|
||||||
option already contains [Option:Some].
|
|
||||||
|
|
||||||
```lua
|
|
||||||
local opt: Option<number> = None()
|
|
||||||
local val: number = opt:insert(1)
|
|
||||||
assert(val == 1)
|
|
||||||
assert(opt:unwrap() == 1)
|
|
||||||
|
|
||||||
local val: number = opt:insert(2)
|
|
||||||
assert(val == 2)
|
|
||||||
```
|
|
||||||
|
|
||||||
@param self Option<T>
|
|
||||||
@param val T -- The value to insert
|
|
||||||
@return T
|
|
||||||
]=]
|
|
||||||
function Option.insert<T>(self: Option<T>, val: T): T
|
|
||||||
self._optValue = val
|
|
||||||
return self._optValue :: T
|
|
||||||
end
|
|
||||||
|
|
||||||
--[=[
|
|
||||||
@within Option
|
|
||||||
|
|
||||||
Inserts value into the option, then returns it.
|
|
||||||
|
|
||||||
If the option already contains a value, the old value is dropped.
|
|
||||||
|
|
||||||
See also [Option:getOrInsert], which doesn’t update the value if the
|
|
||||||
option already contains [Option:Some].
|
|
||||||
|
|
||||||
```lua
|
|
||||||
local opt: Option<number> = None()
|
|
||||||
local val: number = opt:insert(1)
|
|
||||||
assert(val == 1)
|
|
||||||
assert(opt:unwrap() == 1)
|
|
||||||
|
|
||||||
local val: number = opt:insert(2)
|
|
||||||
assert(val == 2)
|
|
||||||
```
|
|
||||||
|
|
||||||
@param self Option<T>
|
|
||||||
@param val T -- The value to insert
|
|
||||||
@return T
|
|
||||||
]=]
|
|
||||||
function Option.getOrInsert<T>(self: Option<T>, val: T): T
|
|
||||||
if self:isNone() then
|
|
||||||
self._optValue = val
|
|
||||||
end
|
|
||||||
|
|
||||||
return self._optValue :: T
|
|
||||||
end
|
|
||||||
|
|
||||||
--[=[
|
|
||||||
@within Option
|
|
||||||
|
|
||||||
Takes the value out of the option, leaving an [Option:None] in its place.
|
|
||||||
|
|
||||||
```lua
|
|
||||||
local x: Option<number> = Some(2)
|
|
||||||
local y: Option<number> = x.take()
|
|
||||||
assert(x == None())
|
|
||||||
assert(y == Some(2))
|
|
||||||
|
|
||||||
local x: Option<number> = None()
|
|
||||||
local y: Option<number> = x.take()
|
|
||||||
assert(x == None())
|
|
||||||
assert(y == None())
|
|
||||||
```
|
|
||||||
|
|
||||||
@param self Option<T>
|
|
||||||
@return Option<T>
|
|
||||||
]=]
|
|
||||||
function Option.take<T>(self: Option<T>): Option<T>
|
|
||||||
if self:isSome() then
|
|
||||||
local val = self._optValue :: T
|
|
||||||
self._optValue = nil
|
|
||||||
return Some(val)
|
|
||||||
end
|
|
||||||
|
|
||||||
return None()
|
|
||||||
end
|
|
||||||
|
|
||||||
--[=[
|
|
||||||
@within Option
|
|
||||||
|
|
||||||
Replaces the actual value in the option by the value given in parameter, returning
|
|
||||||
the old value if present, leaving an [Option:Some] in its place without
|
|
||||||
deinitializing either one.
|
|
||||||
|
|
||||||
```lua
|
|
||||||
local x: Option<number> = Some(2)
|
|
||||||
local old: Option<number> = x:replace(5)
|
|
||||||
assert(x == Some(5))
|
|
||||||
assert(old == Some(2))
|
|
||||||
|
|
||||||
local x: Option<number> = None()
|
|
||||||
local old: Option<number> = x:replace(3)
|
|
||||||
assert(x == Some(3))
|
|
||||||
assert(old == None())
|
|
||||||
```
|
|
||||||
|
|
||||||
@param self Option<T>
|
|
||||||
@param val T
|
|
||||||
@return Option<T>
|
|
||||||
]=]
|
|
||||||
function Option.replace<T>(self: Option<T>, val: T): Option<T>
|
|
||||||
local current: Option<T> = self
|
|
||||||
self._optValue = val
|
|
||||||
|
|
||||||
if current:isNone() then
|
|
||||||
return current
|
|
||||||
end
|
|
||||||
|
|
||||||
return Some(current._optValue :: T)
|
|
||||||
end
|
|
||||||
|
|
||||||
--[=[
|
|
||||||
@within Option
|
|
||||||
|
|
||||||
Returns true if `val` is contained in the [Option:Some].
|
|
||||||
|
|
||||||
```lua
|
|
||||||
local x: Option<number> = Some(2)
|
|
||||||
local y: Option<number> = None()
|
|
||||||
|
|
||||||
assert(x:contains(2))
|
|
||||||
assert(x:contains(4))
|
|
||||||
assert(not y:contains(2))
|
|
||||||
```
|
|
||||||
|
|
||||||
@param self Option<T>
|
|
||||||
@param val T
|
|
||||||
@return boolean
|
|
||||||
]=]
|
|
||||||
function Option.contains<T>(self: Option<T>, val: T): boolean
|
|
||||||
if self:isSome() then
|
|
||||||
return self._optValue == val
|
|
||||||
end
|
|
||||||
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
--[=[
|
|
||||||
@within Option
|
|
||||||
|
|
||||||
Zips `self` with another [Option].
|
|
||||||
|
|
||||||
If `self` is [Option:Some](s) and other is [Option:Some](o), this method returns
|
|
||||||
[Option:Some]({s, o}). Otherwise, [Option:None] is returned.
|
|
||||||
|
|
||||||
```lua
|
|
||||||
local x: Option<number> = Some(1)
|
|
||||||
local y: Option<string> = Some("hi")
|
|
||||||
local z: Option<number> = None()
|
|
||||||
|
|
||||||
assert(x:zip(y) == Some({ 1, "hi" }))
|
|
||||||
assert(x:zip(z) == None())
|
|
||||||
```
|
|
||||||
|
|
||||||
@param self Option<T>
|
|
||||||
@param other Option>U>
|
|
||||||
@return Option<{T | U}>
|
|
||||||
]=]
|
|
||||||
function Option.zip<T, U>(self: Option<T>, other: Option<U>): Option<{ T | U }>
|
|
||||||
if self:isSome() and other:isSome() then
|
|
||||||
return Some({ self._optValue, other._optValue })
|
|
||||||
end
|
|
||||||
|
|
||||||
return None()
|
|
||||||
end
|
|
||||||
|
|
||||||
--[=[
|
|
||||||
@within Option
|
|
||||||
|
|
||||||
Zips `self` and another [Option] with function `op`.
|
|
||||||
|
|
||||||
If `self` is [Option:Some](s) and other is [Option:Some](o), this method returns
|
|
||||||
[Option:Some](op(s, o)). Otherwise, [Option:None] is returned.
|
|
||||||
|
|
||||||
```lua
|
|
||||||
type Point = {
|
|
||||||
x: number,
|
|
||||||
y: number,
|
|
||||||
}
|
|
||||||
local Point: Point & {
|
|
||||||
new: (x: number, y: number) -> Point,
|
|
||||||
} = {}
|
|
||||||
|
|
||||||
function Point.new(x: number, y: number): Point
|
|
||||||
return {
|
|
||||||
x = x,
|
|
||||||
y = y,
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
local xCoord: Option<number> = Some(17.5)
|
|
||||||
local yCoord: Option<number> = Some(42.7)
|
|
||||||
|
|
||||||
assert(xCoord:zipWith(yCoord, Point.new), Some({ x = 17.5, y = 42.7 }))
|
|
||||||
assert(x:zipWith(None(), Point.new), None())
|
|
||||||
```
|
|
||||||
|
|
||||||
@param self Option<T>
|
|
||||||
@param other Option>U>
|
|
||||||
@param op (x: T, y: U) -> R?
|
|
||||||
@return Option<R>
|
|
||||||
]=]
|
|
||||||
function Option.zipWith<T, U, R>(self: Option<T>, other: Option<U>, op: (x: T, y: U) -> R?): Option<R>
|
|
||||||
if self:isSome() and other:isSome() then
|
|
||||||
local computed = op(self._optValue :: T, other._optValue :: U)
|
|
||||||
|
|
||||||
if computed ~= nil then
|
|
||||||
return Some(computed :: R)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return None()
|
|
||||||
end
|
|
||||||
|
|
||||||
--[=[
|
|
||||||
@within Option
|
|
||||||
|
|
||||||
Unzips an option containing a table of two options.
|
|
||||||
|
|
||||||
If `self` is `Some({a, b})` this method returns `(Some(a), Some(b))`.
|
|
||||||
Otherwise, `(None(), None())` is returned.
|
|
||||||
|
|
||||||
```lua
|
|
||||||
local x: Option<{ number | string }> = Some({ 1, "hi" })
|
|
||||||
local y: Option<{ number }> = None()
|
|
||||||
|
|
||||||
assert((x:unzip() == Some(1), Some("hi")))
|
|
||||||
assert((y:unzip() == None(), None()))
|
|
||||||
```
|
|
||||||
|
|
||||||
@param self Option<T>
|
|
||||||
@return (Option<A>, Option<B>)
|
|
||||||
]=]
|
|
||||||
function Option.unzip<T, A, B>(self: Option<T>): (Option<A>, Option<B>)
|
|
||||||
if self:isSome() then
|
|
||||||
if self:isSome() and typeof(self._optValue) == "table" and #self._optValue == 2 then
|
|
||||||
return Some(self._optValue[1] :: A), Some(self._optValue[2] :: B)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return None(), None()
|
|
||||||
end
|
|
||||||
|
|
||||||
--[=[
|
|
||||||
@within Option
|
|
||||||
|
|
||||||
Returns the inner value wrapped by the [Option].
|
|
||||||
|
|
||||||
```lua
|
|
||||||
local x: Option<string> = Some("lol")
|
|
||||||
local y: Option<string> = None()
|
|
||||||
|
|
||||||
assert(x:unwrapUnchecked() == "lol")
|
|
||||||
assert(y:unwrapUnchecked() == nil)
|
|
||||||
```
|
|
||||||
|
|
||||||
@param self Option<T>
|
|
||||||
@return T?
|
|
||||||
]=]
|
|
||||||
function Option.unwrapUnchecked<T>(self: Option<T>): T?
|
|
||||||
return self._optValue
|
|
||||||
end
|
|
||||||
|
|
||||||
--[=[
|
|
||||||
@within Option
|
|
||||||
|
|
||||||
Returns a formatted representation of the option, often
|
|
||||||
used for printing to stdout.
|
|
||||||
|
|
||||||
```lua
|
|
||||||
local x: Option<number> = Some(123)
|
|
||||||
local y: Option<number> = None()
|
|
||||||
|
|
||||||
print(x:display()) -- prints `Option::Some(123)`
|
|
||||||
print(y:display()) -- prints `Option::None`
|
|
||||||
```
|
|
||||||
|
|
||||||
@param self Option<T>
|
|
||||||
@return string
|
|
||||||
]=]
|
|
||||||
function Option.display<T>(self: Option<T>): string
|
|
||||||
return tostring(self)
|
|
||||||
end
|
|
||||||
|
|
||||||
return {
|
|
||||||
Option = Option,
|
|
||||||
Some = Some,
|
|
||||||
None = None,
|
|
||||||
}
|
|
900
rbx/result.luau
900
rbx/result.luau
|
@ -1,900 +0,0 @@
|
||||||
local tableEq = require(script.Parent.util).tableEq
|
|
||||||
|
|
||||||
--[=[
|
|
||||||
@class Result
|
|
||||||
|
|
||||||
Error handling with the [Result] type.
|
|
||||||
|
|
||||||
[Result]`<T, E>` is the type used for returning a possible error. It is a class with the
|
|
||||||
constructor variants, [Result:Ok]`(T)`, representing success and containing a value, and
|
|
||||||
[Result:Err]`(E)`, representing error and containing an error value.
|
|
||||||
|
|
||||||
```lua
|
|
||||||
local result: Result<number, string>
|
|
||||||
|
|
||||||
-- Both variants `Ok` and `Err` fit into the type
|
|
||||||
result = Ok(21)
|
|
||||||
result = Err("some error")
|
|
||||||
```
|
|
||||||
|
|
||||||
Functions return [Result] whenever errors are expected and recoverable.
|
|
||||||
|
|
||||||
A simple function returning [Result] might be defined and used like so:
|
|
||||||
```lua
|
|
||||||
local PAT = "v(%d+)"
|
|
||||||
|
|
||||||
function parseVersion(str: string): Result<number, string>
|
|
||||||
local version: number = string.match(str, PAT)
|
|
||||||
|
|
||||||
if version == 0 then
|
|
||||||
return Err("Version cannot be zero")
|
|
||||||
else if version == nil then
|
|
||||||
return Err("Invalid string")
|
|
||||||
end
|
|
||||||
|
|
||||||
return Ok(version)
|
|
||||||
end
|
|
||||||
|
|
||||||
print("Successful result: " .. parseVersion("v12"):display())
|
|
||||||
print("Error result: " .. parseVersion("vString"):display())
|
|
||||||
```
|
|
||||||
|
|
||||||
[Result] comes with some convenience methods that make working with it more succinct.
|
|
||||||
|
|
||||||
```lua
|
|
||||||
local goodResult: Result<number, number> = Ok(10)
|
|
||||||
local badResult: Result<number, number> = Err(10)
|
|
||||||
|
|
||||||
-- The `isOk`and `isErr` methods do what they say.
|
|
||||||
assert(goodResult:isOk() and not goodResult:isErr())
|
|
||||||
assert(badResult:isErr() and not badResult:isOk())
|
|
||||||
|
|
||||||
-- `map` produces a new result from an existing one
|
|
||||||
goodResult = goodResult:map(function(i) return i + 1 end)
|
|
||||||
badResult = badResult:map(function(i) return i - 1 end)
|
|
||||||
|
|
||||||
-- Use `and_then` to continue the computation.
|
|
||||||
local anotherGoodResult: Result<boolean, number> = goodResult:andThen(function(i) return Ok(i == 11) end)
|
|
||||||
|
|
||||||
-- Use `or_else` to handle the error
|
|
||||||
local badResult: Result<number, number> = bad_result:orElse(function(i) return Ok(i + 20) end)
|
|
||||||
|
|
||||||
-- Get the internal `Ok` value and panic if there is an `Err`
|
|
||||||
local finalAwesomeResult: boolean = anotherGoodResult:unwrap()
|
|
||||||
```
|
|
||||||
]=]
|
|
||||||
local Result = {}
|
|
||||||
|
|
||||||
export type Result<T, E> = typeof(Result) & {
|
|
||||||
_value: T,
|
|
||||||
_error: E,
|
|
||||||
typeId: "Result"
|
|
||||||
}
|
|
||||||
|
|
||||||
--[=[
|
|
||||||
@within Result
|
|
||||||
|
|
||||||
Contains the success value.
|
|
||||||
|
|
||||||
@param val T
|
|
||||||
@return Result<T, E>
|
|
||||||
]=]
|
|
||||||
function Ok<T, E>(val: T): Result<T, E>
|
|
||||||
return Result.new(val, (nil :: unknown) :: E)
|
|
||||||
end
|
|
||||||
|
|
||||||
--[=[
|
|
||||||
@within Result
|
|
||||||
|
|
||||||
Contains the error value.
|
|
||||||
|
|
||||||
@param err E
|
|
||||||
@return Result<T, E>
|
|
||||||
]=]
|
|
||||||
function Err<T, E>(err: E): Result<T, E>
|
|
||||||
return Result.new((nil :: unknown) :: T, err)
|
|
||||||
end
|
|
||||||
|
|
||||||
--[=[
|
|
||||||
@within Result
|
|
||||||
@private
|
|
||||||
|
|
||||||
Constructs a [Result]. **For internal use only**.
|
|
||||||
|
|
||||||
For user consumption, see [Result:Ok] and [Result:Err].
|
|
||||||
|
|
||||||
@param err E
|
|
||||||
@return Result<T, E>
|
|
||||||
]=]
|
|
||||||
function Result.new<T, E>(val: T, err: E)
|
|
||||||
return setmetatable(
|
|
||||||
{
|
|
||||||
_value = val,
|
|
||||||
_error = err,
|
|
||||||
typeId = "Result",
|
|
||||||
} :: Result<T, E>,
|
|
||||||
{
|
|
||||||
__index = Result,
|
|
||||||
__tostring = function<T, E>(self: Result<T, E>)
|
|
||||||
if self:isOk() then
|
|
||||||
return `{self.typeId}::Ok({self._value})`
|
|
||||||
end
|
|
||||||
|
|
||||||
if self:isErr() then
|
|
||||||
return `{self.typeId}::Err({self._error})`
|
|
||||||
end
|
|
||||||
|
|
||||||
return `{self.typeId}<T, E>`
|
|
||||||
end,
|
|
||||||
__eq = function<T, E>(self: Result<T, E>, other: Result<T, E>)
|
|
||||||
if
|
|
||||||
typeof(self._value) ~= "table"
|
|
||||||
and typeof(self._error) ~= "table"
|
|
||||||
and typeof(other._value) ~= "table"
|
|
||||||
and typeof(other._error) ~= "table"
|
|
||||||
then
|
|
||||||
return self._value == other._value and self._error == other._error
|
|
||||||
end
|
|
||||||
|
|
||||||
return tableEq(self._value, other._value) and tableEq(self._error, other._error)
|
|
||||||
end,
|
|
||||||
|
|
||||||
-- TODO: Implement equality and arithmetic metamethods
|
|
||||||
-- TODO: Implement __iter, once iterators traits exist
|
|
||||||
}
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
--[=[
|
|
||||||
@within Result
|
|
||||||
|
|
||||||
Returns `true` if the result is [Result:Ok].
|
|
||||||
|
|
||||||
```lua
|
|
||||||
local x: Result<number, string> = Ok(3)
|
|
||||||
assert(x:isOk())
|
|
||||||
|
|
||||||
x = Err("Some error message")
|
|
||||||
assert(not x:isOk())
|
|
||||||
```
|
|
||||||
|
|
||||||
@param self Result<T, E>
|
|
||||||
@return boolean
|
|
||||||
]=]
|
|
||||||
function Result.isOk<T, E>(self: Result<T, E>): boolean
|
|
||||||
if self._value == nil then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
--[=[
|
|
||||||
@within Result
|
|
||||||
|
|
||||||
Returns `true` if the result is [Result:Ok] and the value inside it matches a
|
|
||||||
predicate.
|
|
||||||
|
|
||||||
```lua
|
|
||||||
local x: Result<number, string> = Ok(2)
|
|
||||||
assert(x:isOkAnd(function(x) return x > 1 end))
|
|
||||||
|
|
||||||
x = Ok(0)
|
|
||||||
assert(not x:isOkAnd(function(x) return x > 1 end))
|
|
||||||
|
|
||||||
x = Err("hey")
|
|
||||||
assert(not x:isOkAnd(function(x) return x > 1 end))
|
|
||||||
```
|
|
||||||
|
|
||||||
@param self Result<T, E>
|
|
||||||
@param predicate (val: T?) -> boolean
|
|
||||||
@return boolean
|
|
||||||
]=]
|
|
||||||
function Result.isOkAnd<T, E>(self: Result<T, E>, predicate: (val: T?) -> boolean): boolean
|
|
||||||
return self:isOk() and predicate(self._value)
|
|
||||||
end
|
|
||||||
|
|
||||||
--[=[
|
|
||||||
@within Result
|
|
||||||
|
|
||||||
Returns `true` if the result is [Result:Err].
|
|
||||||
|
|
||||||
```lua
|
|
||||||
local x: Result<number, string> = Ok(3)
|
|
||||||
assert(not x:isErr())
|
|
||||||
|
|
||||||
x = Err("Some error message")
|
|
||||||
assert(x:isErr())
|
|
||||||
```
|
|
||||||
|
|
||||||
@param self Result<T, E>
|
|
||||||
@return boolean
|
|
||||||
]=]
|
|
||||||
function Result.isErr<T, E>(self: Result<T, E>)
|
|
||||||
return not self:isOk()
|
|
||||||
end
|
|
||||||
|
|
||||||
--[=[
|
|
||||||
@within Result
|
|
||||||
|
|
||||||
Returns `true` if the result is [Result:Err] and the value inside it matches a
|
|
||||||
predicate.
|
|
||||||
|
|
||||||
```lua
|
|
||||||
local x: Result<number, string> = Ok(2)
|
|
||||||
assert(not x:isErrAnd(function(x) return x > 1 end))
|
|
||||||
|
|
||||||
x = Err(3)
|
|
||||||
assert(x:isErrAnd(function(x) return x > 1 end))
|
|
||||||
|
|
||||||
x = Err("hey")
|
|
||||||
assert(not x:isErrAnd(function(x) return x > 1 end))
|
|
||||||
```
|
|
||||||
|
|
||||||
@param self Result<T, E>
|
|
||||||
@param predicate (val: T?) -> boolean
|
|
||||||
@return boolean
|
|
||||||
]=]
|
|
||||||
function Result.isErrAnd<T, E>(self: Result<T, E>, predicate: (val: E) -> boolean): boolean
|
|
||||||
if self:isErr() then
|
|
||||||
return predicate(self._error)
|
|
||||||
end
|
|
||||||
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
--[=[
|
|
||||||
@within Result
|
|
||||||
|
|
||||||
Maps a [Result]<T, E> to [Result]<U, E> by applying a function to a contained [Result:Ok] value,
|
|
||||||
leaving an [Result:Err] value untouched.
|
|
||||||
|
|
||||||
This function can be used to compose the results of two functions.
|
|
||||||
|
|
||||||
```lua
|
|
||||||
local lines = "1\n2\n3\n4\n"
|
|
||||||
|
|
||||||
function parseInt(x: string): Result<number, string>
|
|
||||||
local num = tonumber(x)
|
|
||||||
|
|
||||||
if num == nil then
|
|
||||||
return Err("not an integer")
|
|
||||||
end
|
|
||||||
|
|
||||||
return Ok(num)
|
|
||||||
end
|
|
||||||
|
|
||||||
for line in lines:split("\n") do
|
|
||||||
local number = parseInt(line)
|
|
||||||
|
|
||||||
if number:isOk() then
|
|
||||||
print(number:unwrap())
|
|
||||||
else
|
|
||||||
print("not a number!")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
@param self Result<T, E>
|
|
||||||
@param op (val: T) -> U
|
|
||||||
@return Result<U, E>
|
|
||||||
]=]
|
|
||||||
function Result.map<T, E, U>(self: Result<T, E>, op: (val: T) -> U): Result<U, E>
|
|
||||||
if self:isOk() then
|
|
||||||
return Result.new(op(self._value), self._error) :: Result<U, E>
|
|
||||||
end
|
|
||||||
|
|
||||||
return Err(self._error)
|
|
||||||
end
|
|
||||||
|
|
||||||
--[=[
|
|
||||||
@within Result
|
|
||||||
|
|
||||||
Returns the provided default (if [Result:Err]), or applies a function to
|
|
||||||
the contained value (if [Result:Ok]).
|
|
||||||
|
|
||||||
Arguments passed to [Result:mapOr] are eagerly evaluated; if you are passing the
|
|
||||||
result of a function call, it is recommended to use [Result:mapOrElse], which is
|
|
||||||
lazily evaluated.
|
|
||||||
|
|
||||||
```lua
|
|
||||||
local x: Result<string, string> = Ok("foo")
|
|
||||||
assert(x:mapOr("bar", string.upper) == "FOO")
|
|
||||||
|
|
||||||
x = Err("foo")
|
|
||||||
assert(x:mapOr("bar", string.upper) == "bar")
|
|
||||||
```
|
|
||||||
|
|
||||||
@param self Result<T, E>
|
|
||||||
@param default U
|
|
||||||
@param op (val: T) -> U
|
|
||||||
@return U
|
|
||||||
]=]
|
|
||||||
function Result.mapOr<T, E, U>(self: Result<T, E>, default: U, op: (val: T) -> U): U
|
|
||||||
if self:isOk() then
|
|
||||||
return op(self._value)
|
|
||||||
end
|
|
||||||
|
|
||||||
return default
|
|
||||||
end
|
|
||||||
|
|
||||||
--[=[
|
|
||||||
@within Result
|
|
||||||
|
|
||||||
Maps a [Result]<T, E> to U by applying fallback function default to a contained
|
|
||||||
[Result:Err] value, or function f to a contained [Result:Ok] value.
|
|
||||||
|
|
||||||
This function can be used to unpack a successful result while handling an error.
|
|
||||||
|
|
||||||
```lua
|
|
||||||
local x: Result<string, string> = Ok("foo")
|
|
||||||
assert(x:mapOrElse(function(x) return "error: ".. x end, string.upper) == "FOO")
|
|
||||||
|
|
||||||
x = Err("foo")
|
|
||||||
assert(x:mapOrElse(function(x) return "error: ".. x end, string.upper) == "error: foo")
|
|
||||||
```
|
|
||||||
|
|
||||||
@param self Result<T, E>
|
|
||||||
@param default (val: E) -> U
|
|
||||||
@param op (val: T) -> U
|
|
||||||
@return U
|
|
||||||
]=]
|
|
||||||
function Result.mapOrElse<T, E, U>(self: Result<T, E>, default: (val: E) -> U, op: (val: T) -> U): U
|
|
||||||
if self:isOk() then
|
|
||||||
return op(self._value)
|
|
||||||
end
|
|
||||||
|
|
||||||
return default(self._error)
|
|
||||||
end
|
|
||||||
|
|
||||||
--[=[
|
|
||||||
@within Result
|
|
||||||
|
|
||||||
Maps a [Result]<T, E> to [Result]<T, F> by applying a function to a contained
|
|
||||||
[Result:Err] value, leaving an [Result:Ok] value untouched.
|
|
||||||
|
|
||||||
This function can be used to pass through a successful result while handling an
|
|
||||||
error.
|
|
||||||
|
|
||||||
```lua
|
|
||||||
local function stringify(x: number): string
|
|
||||||
return string.format("error code: %d", x)
|
|
||||||
end
|
|
||||||
|
|
||||||
local x: Result<number, number> = Ok(2)
|
|
||||||
assert(x:mapErr(stringify) == Ok(2))
|
|
||||||
|
|
||||||
x = Err(13)
|
|
||||||
assert(x:mapErr(stringify) == Err("error code: 13"))
|
|
||||||
```
|
|
||||||
|
|
||||||
@param self Result<T, E>
|
|
||||||
@param op (val: E) -> F
|
|
||||||
@return Result<T, F>
|
|
||||||
]=]
|
|
||||||
function Result.mapErr<T, E, F>(self: Result<T, E>, op: (val: E) -> F): Result<T, F>
|
|
||||||
if self:isErr() then
|
|
||||||
return Result.new(self._value, op(self._error))
|
|
||||||
end
|
|
||||||
|
|
||||||
return Ok(self._value)
|
|
||||||
end
|
|
||||||
|
|
||||||
--[=[
|
|
||||||
@within Result
|
|
||||||
|
|
||||||
Calls the provided closure with a reference to the contained value
|
|
||||||
(if [Result:Ok]).
|
|
||||||
|
|
||||||
```lua
|
|
||||||
function parseInt(x: string): Result<number, string>
|
|
||||||
local num = tonumber(x)
|
|
||||||
|
|
||||||
if num == nil then
|
|
||||||
return Err("not an integer")
|
|
||||||
end
|
|
||||||
|
|
||||||
return Ok(num)
|
|
||||||
end
|
|
||||||
|
|
||||||
local x = parseInt("4")
|
|
||||||
:inspect(function(x) print("original: " .. x) end)
|
|
||||||
:map(function(x) return x ^ 3 end)
|
|
||||||
:expect("not an integer")
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
@param self Result<T, E>
|
|
||||||
@param op (val: E) -> ()
|
|
||||||
@return Result<T, E>
|
|
||||||
]=]
|
|
||||||
function Result.inspect<T, E>(self: Result<T, E>, op: (val: T) -> ()): Result<T, E>
|
|
||||||
if self:isOk() then
|
|
||||||
op(self._value)
|
|
||||||
end
|
|
||||||
|
|
||||||
return self
|
|
||||||
end
|
|
||||||
|
|
||||||
--[=[
|
|
||||||
@within Result
|
|
||||||
|
|
||||||
Calls the provided closure with a reference to the contained value
|
|
||||||
(if [Result:Err]).
|
|
||||||
|
|
||||||
```lua
|
|
||||||
function parseInt(x: string): Result<number, string>
|
|
||||||
local num = tonumber(x)
|
|
||||||
|
|
||||||
if num == nil then
|
|
||||||
return Err("not an integer")
|
|
||||||
end
|
|
||||||
|
|
||||||
return Ok(num)
|
|
||||||
end
|
|
||||||
|
|
||||||
local x = parseInt("string")
|
|
||||||
:inspectErr(function(x) print("error: " .. x) end)
|
|
||||||
:map(function(x) return x ^ 3 end)
|
|
||||||
:unwrap()
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
@param self Result<T, E>
|
|
||||||
@param op (val: E) -> ()
|
|
||||||
@return Result<T, E>
|
|
||||||
]=]
|
|
||||||
function Result.inspectErr<T, E>(self: Result<T, E>, op: (val: E) -> ()): Result<T, E>
|
|
||||||
if self:isErr() then
|
|
||||||
op(self._error)
|
|
||||||
end
|
|
||||||
|
|
||||||
return self
|
|
||||||
end
|
|
||||||
|
|
||||||
-- TODO: Iterator traits
|
|
||||||
-- selene: allow(unused_variable)
|
|
||||||
function Result.iter<T, E>(self: Result<T, E>): never
|
|
||||||
return error("Unimplemented: `Result:iter()`")
|
|
||||||
end
|
|
||||||
|
|
||||||
--[=[
|
|
||||||
@within Result
|
|
||||||
|
|
||||||
Returns the contained [Result:Ok] value, consuming the self value.
|
|
||||||
|
|
||||||
Because this function may panic, its use is generally discouraged. Instead,
|
|
||||||
prefer to use [Result:isErr] and handle the Err case explicitly, or call
|
|
||||||
[Result:unwrapOr], [Result:unwrapOrElse], or [Result:unwrapOrDefault].
|
|
||||||
Panics
|
|
||||||
|
|
||||||
@error panic -- If the value is a [Result:Err], with a panic message including the passed message, and the content of the `Err`.
|
|
||||||
|
|
||||||
```lua
|
|
||||||
local x: Result<number, string> = Err("emergency failure")
|
|
||||||
x:expect("Testing expect") -- panics with message `Testing expect: emergency failure`
|
|
||||||
```
|
|
||||||
|
|
||||||
## Recommended Message Style
|
|
||||||
It is recommended that expect messages are used to describe the reason you expect the
|
|
||||||
[Result] should be [Result:Ok].
|
|
||||||
|
|
||||||
```lua
|
|
||||||
local process = require("@lune/process")
|
|
||||||
|
|
||||||
local function envVar<T>(var: string): Result<T, string>
|
|
||||||
local val = process.env[var]
|
|
||||||
|
|
||||||
if val == nil then
|
|
||||||
return Err("environment variable not found")
|
|
||||||
end
|
|
||||||
|
|
||||||
Ok(val)
|
|
||||||
end
|
|
||||||
|
|
||||||
local path = envVar("IMPORTANT_PATH")
|
|
||||||
:expect("env variable `IMPORTANT_PATH` should be set by `wrapper_script.sh`")
|
|
||||||
```
|
|
||||||
|
|
||||||
**Hint**: If you’re having trouble remembering how to phrase expect error messages remember to focus
|
|
||||||
on the word “should” as in “env variable should be set by blah” or “the given binary should be available
|
|
||||||
and executable by the current user”.
|
|
||||||
|
|
||||||
@param self Result<T, E>
|
|
||||||
@param msg string
|
|
||||||
@return T | never
|
|
||||||
]=]
|
|
||||||
function Result.expect<T, E>(self: Result<T, E>, msg: string): T | never
|
|
||||||
if self:isOk() then
|
|
||||||
return self._value
|
|
||||||
end
|
|
||||||
|
|
||||||
return error(`panic: {msg}; {self._error}`)
|
|
||||||
end
|
|
||||||
|
|
||||||
--[=[
|
|
||||||
@within Result
|
|
||||||
|
|
||||||
Returns the [Result:Ok].
|
|
||||||
|
|
||||||
Because this function may panic, its use is generally discouraged. Instead, prefer to use
|
|
||||||
[Result:unwrapOr], [Result:unwrapOrElse], or [Result:unwrapOrDefault].
|
|
||||||
|
|
||||||
@error panic -- Panics if the value is an [Result:Err], with a panic message provided by the [Result:Err]’s value.
|
|
||||||
|
|
||||||
```lua
|
|
||||||
local x: Result<number, string> = Ok(2)
|
|
||||||
assert(x:unwrap() == 2)
|
|
||||||
|
|
||||||
x = Err("oh no")
|
|
||||||
x:unwrap() -- panics with `oh no`
|
|
||||||
```
|
|
||||||
|
|
||||||
@param self Result<T, E>
|
|
||||||
]=]
|
|
||||||
function Result.unwrap<T, E>(self: Result<T, E>): T | never
|
|
||||||
if self:isOk() then
|
|
||||||
return self._value
|
|
||||||
end
|
|
||||||
|
|
||||||
return error(`panic: \`Result:unwrap()\` called on an \`Err\` value: {self._error}`)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- TODO: default values for types
|
|
||||||
-- selene: allow(unused_variable)
|
|
||||||
function Result.unwrapOrDefault<T, E>(self: Result<T, E>): never
|
|
||||||
return error("Unimplemented: `Result:unwrapOrDefault()`")
|
|
||||||
end
|
|
||||||
|
|
||||||
--[=[
|
|
||||||
@within Result
|
|
||||||
|
|
||||||
Returns the contained [Result:Err].
|
|
||||||
|
|
||||||
@error panic -- Panics if the value is an [Result:Ok], with a panic message including the passed message, and the content of the [Resul:Ok].
|
|
||||||
|
|
||||||
```lua
|
|
||||||
local x: Result<number, string> = Ok(10)
|
|
||||||
x:expectErr("Testing expect") -- panics with `Testing expect: 10`
|
|
||||||
```
|
|
||||||
|
|
||||||
@param self Result<T, E>
|
|
||||||
@param msg string
|
|
||||||
@return E | never
|
|
||||||
]=]
|
|
||||||
function Result.expectErr<T, E>(self: Result<T, E>, msg: string): E | never
|
|
||||||
if self:isErr() then
|
|
||||||
return self._error
|
|
||||||
end
|
|
||||||
|
|
||||||
return error(`panic: {msg}; {self._error}`)
|
|
||||||
end
|
|
||||||
|
|
||||||
--[=[
|
|
||||||
@within Result
|
|
||||||
|
|
||||||
Returns the contained [Result:Err].
|
|
||||||
|
|
||||||
@error panic -- Panics if the value is an [Result:Ok], with a panic message provided by the [Result:Ok]’s value.
|
|
||||||
|
|
||||||
```lua
|
|
||||||
local x: Result<number, string> = Ok(2)
|
|
||||||
x:unwrapErr() -- panics with `2`
|
|
||||||
```
|
|
||||||
```lua
|
|
||||||
x = Err("oh no")
|
|
||||||
assert(x:unwrapErr() == "oh no")
|
|
||||||
```
|
|
||||||
|
|
||||||
@param self Result<T, E>
|
|
||||||
@return E | never
|
|
||||||
]=]
|
|
||||||
function Result.unwrapErr<T, E>(self: Result<T, E>): E | never
|
|
||||||
if self:isErr() then
|
|
||||||
return self._error
|
|
||||||
end
|
|
||||||
|
|
||||||
return error(`panic: \`Result:unwrapErr()\` called on an \`Ok\` value: {self._value}`)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- TODO: How the fuck do I implement this?
|
|
||||||
-- selene: allow(unused_variable)
|
|
||||||
function Result.intoOk<T, E>(self: Result<T, E>): never
|
|
||||||
return error("Unimplemented: `Result:intoOk()`")
|
|
||||||
end
|
|
||||||
-- selene: allow(unused_variable)
|
|
||||||
function Result.intoErr<T, E>(self: Result<T, E>): never
|
|
||||||
return error("Unimplemented: `Result:intoErr()`")
|
|
||||||
end
|
|
||||||
|
|
||||||
--[=[
|
|
||||||
@within Result
|
|
||||||
|
|
||||||
Returns res if the result is [Result:Ok], otherwise returns the
|
|
||||||
[Result:Err] value of self.
|
|
||||||
|
|
||||||
```lua
|
|
||||||
local x: Result<number, string> = Ok(2)
|
|
||||||
local y: Result<string, string> = Err("late error")
|
|
||||||
assert(x:and_(y) == Err("late error"))
|
|
||||||
|
|
||||||
local x: Result<number, string> = Err("early error")
|
|
||||||
local y: Result<string, string> = Ok("foo")
|
|
||||||
assert(x:and_(y) == Err("early error"))
|
|
||||||
|
|
||||||
local x: Result<number, string> = Err("not a 2")
|
|
||||||
local y: Result<string, string> = Err("late error")
|
|
||||||
assert(x:and_(y) == Err("not a 2"))
|
|
||||||
|
|
||||||
local x: Result<number, string> = Ok(2)
|
|
||||||
local y: Result<string, string> = Ok("different result type")
|
|
||||||
assert(x:and_(y) == Ok("different result type"))
|
|
||||||
```
|
|
||||||
|
|
||||||
@param self Result<T, E>
|
|
||||||
@param res Result<U, E>
|
|
||||||
@return Result<U, E>
|
|
||||||
]=]
|
|
||||||
function Result.and_<T, E, U>(self: Result<T, E>, res: Result<U, E>): Result<U, E>
|
|
||||||
if self:isOk() then
|
|
||||||
return res
|
|
||||||
end
|
|
||||||
|
|
||||||
return Err(self._error)
|
|
||||||
end
|
|
||||||
|
|
||||||
--[=[
|
|
||||||
@within Result
|
|
||||||
|
|
||||||
Calls `op` if the result is [Result:Ok], otherwise returns the
|
|
||||||
[Result:Err] value of `self`.
|
|
||||||
|
|
||||||
This function can be used for control flow based on [Result]
|
|
||||||
values.
|
|
||||||
|
|
||||||
```lua
|
|
||||||
function sqThenToString(x): Result<string, string>
|
|
||||||
if typeof(sq) ~= "number" then
|
|
||||||
return Err("not a number: '" .. x .. "'")
|
|
||||||
end
|
|
||||||
|
|
||||||
return Ok(x ^ 2):map(function(sq)
|
|
||||||
return tostring(sq)
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
assert(Ok(2):andThen(sqThenToString) == Ok("4"))
|
|
||||||
assert(Err("string"):andThen(sqThenToString) == Err("not a number: 'string'"))
|
|
||||||
```
|
|
||||||
|
|
||||||
Often used to chain fallible operations that may return [Result:Err].
|
|
||||||
|
|
||||||
@param self Result<T, E>
|
|
||||||
@param op (...any) -> any
|
|
||||||
@return Result<U, E>
|
|
||||||
]=]
|
|
||||||
function Result.andThen<T, E, U>(self: Result<T, E>, op: (...any) -> any): Result<U, E>
|
|
||||||
if self:isOk() then
|
|
||||||
return op(self._value)
|
|
||||||
end
|
|
||||||
|
|
||||||
return Err(self._error) :: Result<U, E>
|
|
||||||
end
|
|
||||||
|
|
||||||
--[=[
|
|
||||||
@within Result
|
|
||||||
|
|
||||||
Calls `op` if the result is [Result:Ok], otherwise returns the
|
|
||||||
[Result:Err] value of `self`.
|
|
||||||
|
|
||||||
This function can be used for control flow based on [Result]
|
|
||||||
values.
|
|
||||||
|
|
||||||
```lua
|
|
||||||
local x: Result<number, string> = Ok(2)
|
|
||||||
local y: Result<number, string> = Err("late error")
|
|
||||||
assert(x:or_(y) == Ok(2))
|
|
||||||
|
|
||||||
local x: Result<number, string> = Err("early error")
|
|
||||||
local y: Result<number, string> = Ok(2)
|
|
||||||
assert(x:or_(y) == Ok(2))
|
|
||||||
|
|
||||||
local x: Result<number, string> = Err("not a 2")
|
|
||||||
local y: Result<number, string> = Err("late error")
|
|
||||||
assert(x:or_(y) == Err("late error"))
|
|
||||||
|
|
||||||
local x: Result<number, string> = Ok(2)
|
|
||||||
local y: Result<number, string> = Ok(100)
|
|
||||||
assert(x:or_(y) == Ok(2))
|
|
||||||
```
|
|
||||||
|
|
||||||
@param self Result<T, E>
|
|
||||||
@param res Result<T, F>
|
|
||||||
@return Result<T, F> | never
|
|
||||||
]=]
|
|
||||||
function Result.or_<T, E, F>(self: Result<T, E>, res: Result<T, F>): Result<T, F> | never
|
|
||||||
if self:isErr() then
|
|
||||||
return res
|
|
||||||
end
|
|
||||||
|
|
||||||
if self:isOk() then
|
|
||||||
return Ok(self._value) :: Result<T, F>
|
|
||||||
end
|
|
||||||
|
|
||||||
return error("called `Result:or()` with an invalid value")
|
|
||||||
end
|
|
||||||
|
|
||||||
--[=[
|
|
||||||
@within Result
|
|
||||||
|
|
||||||
Calls `op` if the result is [Result:Ok], otherwise returns the
|
|
||||||
[Result:Err] value of `self`.
|
|
||||||
|
|
||||||
This function can be used for control flow based on [Result]
|
|
||||||
values.
|
|
||||||
|
|
||||||
```lua
|
|
||||||
function sq(x: number): Result<number, number>
|
|
||||||
return Ok(x * x)
|
|
||||||
end
|
|
||||||
|
|
||||||
function err(x: number): Result<number, number>
|
|
||||||
return Err(x)
|
|
||||||
end
|
|
||||||
|
|
||||||
assert(Ok(2):orElse(sq):orElse(sq) == Ok(2))
|
|
||||||
assert(Ok(2):orElse(err):orElse(sq) == Ok(2))
|
|
||||||
assert(Err(3):orElse(sq):orElse(err) == Ok(9))
|
|
||||||
assert(Err(3):orElse(err):orElse(err) == Err(3))
|
|
||||||
```
|
|
||||||
|
|
||||||
@param self Result<T, E>
|
|
||||||
@param op (x: E) -> Result<T, U>
|
|
||||||
@return Result<T, U>
|
|
||||||
]=]
|
|
||||||
function Result.orElse<T, E, U>(self: Result<T, E>, op: (x: E) -> Result<T, U>): Result<T, U>
|
|
||||||
if self:isErr() then
|
|
||||||
return op(self._error)
|
|
||||||
end
|
|
||||||
|
|
||||||
return Ok(self._value)
|
|
||||||
end
|
|
||||||
|
|
||||||
--[=[
|
|
||||||
@within Result
|
|
||||||
|
|
||||||
Returns the contained [Result:Ok] value or a provided default.
|
|
||||||
|
|
||||||
Arguments passed to [Result:unwrapOr] are eagerly evaluated; if you are passing the
|
|
||||||
result of a function call, it is recommended to use [Result:unwrapOrElse], which is
|
|
||||||
lazily evaluated.
|
|
||||||
|
|
||||||
```lua
|
|
||||||
local default = 2
|
|
||||||
local x: Result<number, string> = Ok(9)
|
|
||||||
assert(x:unwrapOr(default), 9)
|
|
||||||
|
|
||||||
x = Err("error")
|
|
||||||
assert(x:unwrapOr(default), default)
|
|
||||||
```
|
|
||||||
|
|
||||||
@param self Result<T, E>
|
|
||||||
@param default T
|
|
||||||
@return T
|
|
||||||
]=]
|
|
||||||
function Result.unwrapOr<T, E>(self: Result<T, E>, default: T): T
|
|
||||||
if self:isOk() then
|
|
||||||
return self._value
|
|
||||||
end
|
|
||||||
|
|
||||||
return default
|
|
||||||
end
|
|
||||||
|
|
||||||
--[=[
|
|
||||||
@within Result
|
|
||||||
|
|
||||||
Returns the contained [Result:Ok] value or computes it from a closure.
|
|
||||||
|
|
||||||
```lua
|
|
||||||
function count(x: string): number
|
|
||||||
return #x
|
|
||||||
end
|
|
||||||
|
|
||||||
assert(Ok(2):unwrapOrElse(count) == 2))
|
|
||||||
assert(Err("foo"):unwrapOrElse(count) == 3))
|
|
||||||
```
|
|
||||||
|
|
||||||
@param self Result<T, E>
|
|
||||||
@param op (x: E) -> T
|
|
||||||
@return T
|
|
||||||
]=]
|
|
||||||
function Result.unwrapOrElse<T, E>(self: Result<T, E>, op: (x: E) -> T): T
|
|
||||||
if self:isOk() then
|
|
||||||
return self._value
|
|
||||||
end
|
|
||||||
|
|
||||||
return op(self._error)
|
|
||||||
end
|
|
||||||
|
|
||||||
--[=[
|
|
||||||
@within Result
|
|
||||||
|
|
||||||
Returns true if the [Result:Ok] value is `val`.
|
|
||||||
|
|
||||||
```lua
|
|
||||||
local x: Result<number, string> = Ok(2)
|
|
||||||
assert(x:contains(2))
|
|
||||||
|
|
||||||
x = Ok(3)
|
|
||||||
assert(not x:contains(2))
|
|
||||||
|
|
||||||
x = Err(2)
|
|
||||||
assert(not x:contains(2))
|
|
||||||
```
|
|
||||||
|
|
||||||
@param self Result<T, E>
|
|
||||||
@param val T
|
|
||||||
@return boolean
|
|
||||||
]=]
|
|
||||||
function Result.contains<T, E>(self: Result<T, E>, val: T): boolean
|
|
||||||
if self:isOk() then
|
|
||||||
return self._value == val
|
|
||||||
end
|
|
||||||
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
--[=[
|
|
||||||
@within Result
|
|
||||||
|
|
||||||
Returns true if the [Result:Err] value is `err`.
|
|
||||||
|
|
||||||
```lua
|
|
||||||
local x: Result<number, string> = Err(2)
|
|
||||||
assert(x:containsErr(2))
|
|
||||||
|
|
||||||
x = Err(3)
|
|
||||||
assert(not x:containsErr(2))
|
|
||||||
|
|
||||||
x = Ok(2)
|
|
||||||
assert(not x:containsErr(2))
|
|
||||||
```
|
|
||||||
|
|
||||||
@param self Result<T, E>
|
|
||||||
@param err E
|
|
||||||
@return boolean
|
|
||||||
]=]
|
|
||||||
function Result.containsErr<T, E>(self: Result<T, E>, err: E): boolean
|
|
||||||
if self:isErr() then
|
|
||||||
return self._error == err
|
|
||||||
end
|
|
||||||
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
--[=[
|
|
||||||
@within Result
|
|
||||||
|
|
||||||
Returns a formatted representation of the result, often
|
|
||||||
used for printing to stdout.
|
|
||||||
|
|
||||||
```lua
|
|
||||||
local x: Result<number, string> = Ok(123)
|
|
||||||
local y: Result<number, string> = Err("error")
|
|
||||||
|
|
||||||
print(x:display()) -- prints `Result::Ok(123)`
|
|
||||||
print(y:display()) -- prints `Result::Err("error")`
|
|
||||||
```
|
|
||||||
|
|
||||||
@param self Result<T, E>
|
|
||||||
@return string
|
|
||||||
]=]
|
|
||||||
function Result.display<T, E>(self: Result<T, E>): string
|
|
||||||
return tostring(self)
|
|
||||||
end
|
|
||||||
|
|
||||||
return {
|
|
||||||
Ok = Ok,
|
|
||||||
Err = Err,
|
|
||||||
Result = Result,
|
|
||||||
}
|
|
102
rbx/util.luau
102
rbx/util.luau
|
@ -1,102 +0,0 @@
|
||||||
local isRoblox = _VERSION == "Luau"
|
|
||||||
local isLune = _VERSION:find("Lune") == 1
|
|
||||||
local deps = if isLune then require(script.Parent.Parent.deps)elseif isRoblox then require(script.Parent.deps) else error("Unsupported Runtime!")
|
|
||||||
local requires = deps(isRoblox, isLune)
|
|
||||||
|
|
||||||
local LuauSignal: {
|
|
||||||
new: <T...>() -> Signal<T...>
|
|
||||||
} = requires.signal
|
|
||||||
|
|
||||||
export type Signal<T...> = {
|
|
||||||
Fire: (self: Signal<T...>,T...) -> (),
|
|
||||||
Connect: (self: Signal<T...>, callback: (T...) -> ()) -> () -> (),
|
|
||||||
Once: (self: Signal<T...>, callback: (T...) -> ()) -> () -> (),
|
|
||||||
DisconnectAll: (self: Signal<T...>) -> ()
|
|
||||||
}
|
|
||||||
|
|
||||||
-- From https://gist.github.com/sapphyrus/fd9aeb871e3ce966cc4b0b969f62f539
|
|
||||||
local function tableEq(tbl1, tbl2)
|
|
||||||
-- if tbl1 == tbl2 then
|
|
||||||
-- return true
|
|
||||||
-- elseif type(tbl1) == "table" and type(tbl2) == "table" then
|
|
||||||
-- for key1, value1 in pairs(tbl1) do
|
|
||||||
-- local value2 = tbl2[key1]
|
|
||||||
|
|
||||||
-- if value2 == nil then
|
|
||||||
-- -- avoid the type call for missing keys in tbl2 by directly comparing with nil
|
|
||||||
-- return false
|
|
||||||
-- elseif value1 ~= value2 then
|
|
||||||
-- if type(value1) == "table" and type(value2) == "table" then
|
|
||||||
-- if not tableEq(value1, value2) then
|
|
||||||
-- return false
|
|
||||||
-- end
|
|
||||||
-- else
|
|
||||||
-- return false
|
|
||||||
-- end
|
|
||||||
-- end
|
|
||||||
-- end
|
|
||||||
|
|
||||||
-- -- check for missing keys in tbl1
|
|
||||||
-- for key2, _ in pairs(tbl2) do
|
|
||||||
-- if tbl1[key2] == nil then
|
|
||||||
-- return false
|
|
||||||
-- end
|
|
||||||
-- end
|
|
||||||
|
|
||||||
-- return true
|
|
||||||
-- end
|
|
||||||
|
|
||||||
-- return false
|
|
||||||
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
local RobloxEvent = {}
|
|
||||||
type RobloxEvent<T...> = Signal<T...> & {
|
|
||||||
_inner: BindableEvent
|
|
||||||
}
|
|
||||||
|
|
||||||
function RobloxEvent.new<T...>()
|
|
||||||
local instance = Instance.new("BindableEvent")
|
|
||||||
instance.Parent = script
|
|
||||||
instance.Name = tostring({}):split(" ")[2]
|
|
||||||
|
|
||||||
return setmetatable({
|
|
||||||
_inner = instance
|
|
||||||
}, {
|
|
||||||
__index = RobloxEvent
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
function RobloxEvent.Fire<T...>(self: RobloxEvent<T...>, ...: T...)
|
|
||||||
return self._inner:Fire(...)
|
|
||||||
end
|
|
||||||
|
|
||||||
function RobloxEvent.Connect<T...>(self: RobloxEvent<T...>, callback: (T...) -> ())
|
|
||||||
local conn = self._inner.Event:Connect(callback)
|
|
||||||
|
|
||||||
return {
|
|
||||||
DisconnectAll = function<T...>(self: RobloxEvent<T...>)
|
|
||||||
conn:Disconnect()
|
|
||||||
self._inner:Destroy()
|
|
||||||
end
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
function RobloxEvent.Once<T...>(self: RobloxEvent<T...>, callback: (T...) -> ())
|
|
||||||
local conn = self._inner.Event:Connect(callback)
|
|
||||||
|
|
||||||
return {
|
|
||||||
DisconnectAll = function<T...>(self: RobloxEvent<T...>)
|
|
||||||
conn:Disconnect()
|
|
||||||
self._inner:Destroy()
|
|
||||||
end
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
return {
|
|
||||||
tableEq = tableEq,
|
|
||||||
Signal = setmetatable({}, {
|
|
||||||
__index = if isLune then LuauSignal elseif isRoblox then RobloxEvent else error("Unsupported runtime!"),
|
|
||||||
}),
|
|
||||||
}
|
|
Loading…
Reference in a new issue