mirror of
https://github.com/pesde-pkg/tooling.git
synced 2025-01-08 07:29:10 +00:00
refactor(lib): use extension pattern for result<->option
Formerly, we used metatables to get custom `Option` and `Result` objects which were difficult to type properly, leading to a lot of `unknown` and `any` casts. This refactor fixes it by making extensions opt-in, where we import the extension methods separately from the original implementations, thereby allowing us to not have to typecast things everywhere.
This commit is contained in:
parent
45627ea4a9
commit
ead60c003e
15 changed files with 96 additions and 86 deletions
|
@ -7,11 +7,11 @@ local pathfs = require("../lune_packages/pathfs")
|
||||||
local base64 = require("../lune_packages/base64")
|
local base64 = require("../lune_packages/base64")
|
||||||
|
|
||||||
local manifestTypes = require("../toolchainlib/src/manifest")
|
local manifestTypes = require("../toolchainlib/src/manifest")
|
||||||
local types = require("../toolchainlib/src/utils/result_option_conv")
|
local Result = require("../lune_packages/result")
|
||||||
local Option = types.Option
|
local Option = require("../lune_packages/option")
|
||||||
type Option<T> = types.Option<T>
|
type Result<T, E> = Result.Result<T, E>
|
||||||
local Result = types.Result
|
type Option<T> = Option.Option<T>
|
||||||
type Result<T, E> = types.Result<T, E>
|
|
||||||
local Github = require("../toolchainlib/src/github")
|
local Github = require("../toolchainlib/src/github")
|
||||||
|
|
||||||
type GithubContents = {
|
type GithubContents = {
|
||||||
|
|
18
pesde.lock
18
pesde.lock
|
@ -65,6 +65,24 @@ index_url = "https://github.com/daimond113/pesde-index"
|
||||||
environment = "lune"
|
environment = "lune"
|
||||||
lib = "lib/init.luau"
|
lib = "lib/init.luau"
|
||||||
|
|
||||||
|
[graph."lukadev_0/result"."1.2.0 lune"]
|
||||||
|
direct = ["result", { name = "lukadev_0/result", version = "^1.2.0" }, "dev"]
|
||||||
|
resolved_ty = "dev"
|
||||||
|
|
||||||
|
[graph."lukadev_0/result"."1.2.0 lune".target]
|
||||||
|
environment = "lune"
|
||||||
|
lib = "lib/init.luau"
|
||||||
|
|
||||||
|
[graph."lukadev_0/result"."1.2.0 lune".pkg_ref]
|
||||||
|
ref_ty = "pesde"
|
||||||
|
name = "lukadev_0/result"
|
||||||
|
version = "1.2.0"
|
||||||
|
index_url = "https://github.com/daimond113/pesde-index"
|
||||||
|
|
||||||
|
[graph."lukadev_0/result"."1.2.0 lune".pkg_ref.target]
|
||||||
|
environment = "lune"
|
||||||
|
lib = "lib/init.luau"
|
||||||
|
|
||||||
[graph."synpixel/base64"."3.0.1 lune"]
|
[graph."synpixel/base64"."3.0.1 lune"]
|
||||||
direct = ["base64", { name = "synpixel/base64", version = "^3.0.1" }, "dev"]
|
direct = ["base64", { name = "synpixel/base64", version = "^3.0.1" }, "dev"]
|
||||||
resolved_ty = "dev"
|
resolved_ty = "dev"
|
||||||
|
|
|
@ -9,6 +9,7 @@ environment = "lune"
|
||||||
|
|
||||||
[dev_dependencies]
|
[dev_dependencies]
|
||||||
option = { name = "lukadev_0/option", version = "^1.2.0" }
|
option = { name = "lukadev_0/option", version = "^1.2.0" }
|
||||||
|
result = { name = "lukadev_0/result", version = "^1.2.0" }
|
||||||
pathfs = { name = "jiwonz/pathfs", version = "^0.1.0" }
|
pathfs = { name = "jiwonz/pathfs", version = "^0.1.0" }
|
||||||
base64 = { name = "synpixel/base64", version = "^3.0.1" }
|
base64 = { name = "synpixel/base64", version = "^3.0.1" }
|
||||||
|
|
||||||
|
|
|
@ -4,13 +4,13 @@ local process = require("@lune/process")
|
||||||
local dirs = require("../lune_packages/dirs")
|
local dirs = require("../lune_packages/dirs")
|
||||||
local pathfs = require("../lune_packages/pathfs")
|
local pathfs = require("../lune_packages/pathfs")
|
||||||
|
|
||||||
|
local Result = require("../lune_packages/result")
|
||||||
|
local Option = require("../lune_packages/option")
|
||||||
|
type Result<T, E> = Result.Result<T, E>
|
||||||
|
type Option<T> = Option.Option<T>
|
||||||
|
|
||||||
local revTable = require("./utils/rev_table")
|
local revTable = require("./utils/rev_table")
|
||||||
local CommandBuilder = require("./utils/exec")
|
local CommandBuilder = require("./utils/exec")
|
||||||
local types = require("./utils/result_option_conv")
|
|
||||||
local Option = types.Option
|
|
||||||
type Option<T> = types.Option<T>
|
|
||||||
local Result = types.Result
|
|
||||||
type Result<T, E> = types.Result<T, E>
|
|
||||||
|
|
||||||
export type CompressionFormat = "TarGz" | "TarXz" | "Zip"
|
export type CompressionFormat = "TarGz" | "TarXz" | "Zip"
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
local net = require("@lune/net")
|
local net = require("@lune/net")
|
||||||
|
|
||||||
|
local Result = require("../lune_packages/result")
|
||||||
|
local Option = require("../lune_packages/option")
|
||||||
|
type Result<T, E> = Result.Result<T, E>
|
||||||
|
type Option<T> = Option.Option<T>
|
||||||
|
|
||||||
local copy = require("./utils/copy")
|
local copy = require("./utils/copy")
|
||||||
local types = require("./utils/result_option_conv")
|
|
||||||
local Option = types.Option
|
|
||||||
local Result = types.Result
|
|
||||||
type Option<T> = types.Option<T>
|
|
||||||
type Result<T, E> = types.Result<T, E>
|
|
||||||
|
|
||||||
local Github = {}
|
local Github = {}
|
||||||
export type Github = typeof(setmetatable(Github :: GithubFields, { __index = Github }))
|
export type Github = typeof(setmetatable(Github :: GithubFields, { __index = Github }))
|
||||||
|
|
|
@ -15,9 +15,11 @@ local serde = require("@lune/serde")
|
||||||
local pathfs = require("../lune_packages/pathfs")
|
local pathfs = require("../lune_packages/pathfs")
|
||||||
local dirs = require("../lune_packages/dirs")
|
local dirs = require("../lune_packages/dirs")
|
||||||
|
|
||||||
local types = require("./utils/result_option_conv")
|
local Result = require("../lune_packages/result")
|
||||||
local Option = types.Option
|
local ResultExt = require("./utils/ext/result")
|
||||||
type Option<T> = types.Option<T>
|
local Option = require("../lune_packages/option")
|
||||||
|
type Result<T, E> = Result.Result<T, E>
|
||||||
|
type Option<T> = Option.Option<T>
|
||||||
|
|
||||||
local Github = require("./github")
|
local Github = require("./github")
|
||||||
local PlatformDescriptor = require("./platform/descriptor")
|
local PlatformDescriptor = require("./platform/descriptor")
|
||||||
|
@ -54,7 +56,7 @@ local function downloadAndDecompress(asset: {
|
||||||
return error(`Failed to download asset {asset.name}: HTTP Code {contentsResp.statusCode}`)
|
return error(`Failed to download asset {asset.name}: HTTP Code {contentsResp.statusCode}`)
|
||||||
end
|
end
|
||||||
|
|
||||||
return compression.decompress[format](buffer.fromstring(contentsResp.body)):ok() :: Option<pathfs.Path>
|
return ResultExt.ok(compression.decompress[format](buffer.fromstring(contentsResp.body)))
|
||||||
end) :: Option<pathfs.Path>
|
end) :: Option<pathfs.Path>
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -89,7 +91,9 @@ function runTool(tool: ToolId | pathfs.Path): number
|
||||||
-- FIXME: `process.spawn` has a bug where interactive features don't
|
-- FIXME: `process.spawn` has a bug where interactive features don't
|
||||||
-- forward properly
|
-- forward properly
|
||||||
local toolId = tool :: ToolId
|
local toolId = tool :: ToolId
|
||||||
local path = if toolId.alias ~= nil then LINK_INSTALL_DIR:join(toolAliasOrDefault(toolId)) else tool :: pathfs.Path
|
local path = if (toolId :: any).alias ~= nil
|
||||||
|
then LINK_INSTALL_DIR:join(toolAliasOrDefault(toolId))
|
||||||
|
else tool :: pathfs.Path
|
||||||
|
|
||||||
return process.spawn(path:toString(), process.args, {
|
return process.spawn(path:toString(), process.args, {
|
||||||
cwd = process.cwd,
|
cwd = process.cwd,
|
||||||
|
|
|
@ -4,9 +4,8 @@
|
||||||
local process = require("@lune/process")
|
local process = require("@lune/process")
|
||||||
local detection = require("./detection")
|
local detection = require("./detection")
|
||||||
|
|
||||||
local types = require("../utils/result_option_conv")
|
local Option = require("../../lune_packages/option")
|
||||||
local Option = types.Option
|
type Option<T> = Option.Option<T>
|
||||||
type Option<T> = types.Option<T>
|
|
||||||
|
|
||||||
export type Arch = process.Arch | "arm" | "x86"
|
export type Arch = process.Arch | "arm" | "x86"
|
||||||
|
|
||||||
|
|
|
@ -7,11 +7,11 @@ local toolchain = require("./toolchain")
|
||||||
local result = require("./result")
|
local result = require("./result")
|
||||||
local detectFromExecutable = require("./detection/executable")
|
local detectFromExecutable = require("./detection/executable")
|
||||||
|
|
||||||
local types = require("../utils/result_option_conv")
|
local Result = require("../../lune_packages/result")
|
||||||
local Option = types.Option
|
local Option = require("../../lune_packages/option")
|
||||||
local Result = types.Result
|
local OptionExt = require("../utils/ext/option")
|
||||||
type Option<T> = types.Option<T>
|
type Result<T, E> = Result.Result<T, E>
|
||||||
type Result<T, E> = types.Result<T, E>
|
type Option<T> = Option.Option<T>
|
||||||
|
|
||||||
local PlatformDescriptor = {}
|
local PlatformDescriptor = {}
|
||||||
export type PlatformDescriptor = {
|
export type PlatformDescriptor = {
|
||||||
|
@ -63,7 +63,8 @@ function PlatformDescriptor.fromExecutable(path: string): result.PlatformResult<
|
||||||
}
|
}
|
||||||
end) :: Option<PlatformDescriptor>
|
end) :: Option<PlatformDescriptor>
|
||||||
|
|
||||||
return platformDesc:okOr(
|
return OptionExt.okOr(
|
||||||
|
platformDesc,
|
||||||
"NoExecutableDetected" :: result.PlatformError
|
"NoExecutableDetected" :: result.PlatformError
|
||||||
) :: result.PlatformResult<PlatformDescriptor>
|
) :: result.PlatformResult<PlatformDescriptor>
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
local String = require("../../utils/string")
|
local String = require("../../utils/string")
|
||||||
|
|
||||||
local types = require("../../utils/result_option_conv")
|
local Option = require("../../../lune_packages/option")
|
||||||
local Option = types.Option
|
type Option<T> = Option.Option<T>
|
||||||
type Option<T> = types.Option<T>
|
|
||||||
|
|
||||||
local function charWordSep(char: string)
|
local function charWordSep(char: string)
|
||||||
return char == " " or char == "-" or char == "_"
|
return char == " " or char == "-" or char == "_"
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
local types = require("../utils/result_option_conv")
|
local Result = require("../../lune_packages/result")
|
||||||
type Result<T, E> = types.Result<T, E>
|
type Result<T, E> = Result.Result<T, E>
|
||||||
|
|
||||||
export type PlatformError = "NoPatternDetected" | "NoExecutableDetected" | "UnknownExecutableField"
|
export type PlatformError = "NoPatternDetected" | "NoExecutableDetected" | "UnknownExecutableField"
|
||||||
export type PlatformResult<T> = Result<T, PlatformError>
|
export type PlatformResult<T> = Result<T, PlatformError>
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
local types = require("../utils/result_option_conv")
|
local Option = require("../../lune_packages/option")
|
||||||
local Option = types.Option
|
type Option<T> = Option.Option<T>
|
||||||
type Option<T> = types.Option<T>
|
|
||||||
|
|
||||||
local TOOLCHAINS: { Toolchain } = { "msvc", "gnu", "musl" }
|
local TOOLCHAINS: { Toolchain } = { "msvc", "gnu", "musl" }
|
||||||
export type Toolchain = "msvc" | "gnu" | "musl"
|
export type Toolchain = "msvc" | "gnu" | "musl"
|
||||||
|
|
|
@ -3,9 +3,8 @@
|
||||||
local process = require("@lune/process")
|
local process = require("@lune/process")
|
||||||
local task = require("@lune/task")
|
local task = require("@lune/task")
|
||||||
|
|
||||||
local types = require("../utils/result_option_conv")
|
local Option = require("../../lune_packages/option")
|
||||||
local Option = types.Option
|
type Option<T> = Option.Option<T>
|
||||||
type Option<T> = types.Option<T>
|
|
||||||
|
|
||||||
local CommandBuilder = {}
|
local CommandBuilder = {}
|
||||||
type CommandBuilderFields = {
|
type CommandBuilderFields = {
|
||||||
|
@ -34,10 +33,9 @@ export type ChildStatus = { ok: boolean, code: number, io: {
|
||||||
stderr: string,
|
stderr: string,
|
||||||
} }
|
} }
|
||||||
|
|
||||||
-- FIXME: remove unknown usage
|
|
||||||
local DEFAULT_STDIO_STRATEGY: IoStrategyMapping = {
|
local DEFAULT_STDIO_STRATEGY: IoStrategyMapping = {
|
||||||
stdout = Option.Some("pipe" :: StdioStrategy) :: Option<unknown>,
|
stdout = Option.Some("pipe" :: StdioStrategy),
|
||||||
stderr = Option.Some("pipe" :: StdioStrategy) :: Option<unknown>,
|
stderr = Option.Some("pipe" :: StdioStrategy),
|
||||||
}
|
}
|
||||||
local DEFAULT_RETRIES = 0
|
local DEFAULT_RETRIES = 0
|
||||||
local DEFAULT_IGNORE_ERRORS = false
|
local DEFAULT_IGNORE_ERRORS = false
|
||||||
|
@ -84,11 +82,10 @@ function CommandBuilder.withStdioStrategy(
|
||||||
self: CommandBuilder,
|
self: CommandBuilder,
|
||||||
strategy: StdioStrategy | IoStrategyMapping
|
strategy: StdioStrategy | IoStrategyMapping
|
||||||
): CommandBuilder
|
): CommandBuilder
|
||||||
-- FIXME: remove unknown usage
|
|
||||||
self.stdioStrategy = Option.Some(if typeof(strategy) == "string"
|
self.stdioStrategy = Option.Some(if typeof(strategy) == "string"
|
||||||
then {
|
then {
|
||||||
stdout = Option.Some(strategy) :: Option<unknown>,
|
stdout = Option.Some(strategy),
|
||||||
stderr = Option.Some(strategy) :: Option<unknown>,
|
stderr = Option.Some(strategy),
|
||||||
}
|
}
|
||||||
else strategy) :: Option<IoStrategyMapping>
|
else strategy) :: Option<IoStrategyMapping>
|
||||||
return self
|
return self
|
||||||
|
@ -124,12 +121,8 @@ function CommandBuilder.intoChildProcess(self: CommandBuilder): ChildProcess
|
||||||
else `{self.program} {argsList} & echo $!`,
|
else `{self.program} {argsList} & echo $!`,
|
||||||
{},
|
{},
|
||||||
{
|
{
|
||||||
stdio = self
|
stdio = self.stdioStrategy
|
||||||
.stdioStrategy
|
:orOpt(Option.Some(DEFAULT_STDIO_STRATEGY))
|
||||||
-- FIXME: remove unknown usage
|
|
||||||
:orOpt(
|
|
||||||
Option.Some(DEFAULT_STDIO_STRATEGY) :: Option<unknown>
|
|
||||||
)
|
|
||||||
:map(function(mappings: IoStrategyMapping)
|
:map(function(mappings: IoStrategyMapping)
|
||||||
local translatedMappings: process.SpawnOptionsStdio = {}
|
local translatedMappings: process.SpawnOptionsStdio = {}
|
||||||
for field, value in mappings do
|
for field, value in mappings do
|
||||||
|
|
16
toolchainlib/src/utils/ext/option.luau
Normal file
16
toolchainlib/src/utils/ext/option.luau
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
--> Non-exhaustive set of extensions for the `Option<T>` type
|
||||||
|
|
||||||
|
local Option = require("../../../lune_packages/option")
|
||||||
|
local Result = require("../../../lune_packages/result")
|
||||||
|
|
||||||
|
local OptionExt = {}
|
||||||
|
|
||||||
|
function OptionExt.okOr<T, E>(self: Option.Option<T>, err: E): Result.Result<T, E>
|
||||||
|
return self:mapOrElse(function()
|
||||||
|
return Result.Err(err)
|
||||||
|
end, function(val)
|
||||||
|
return Result.Ok(val)
|
||||||
|
end) :: Result.Result<T, E>
|
||||||
|
end
|
||||||
|
|
||||||
|
return OptionExt
|
14
toolchainlib/src/utils/ext/result.luau
Normal file
14
toolchainlib/src/utils/ext/result.luau
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
--> Non-exhaustive set of extensions for the `Result<T, E>` type
|
||||||
|
|
||||||
|
local Option = require("../../../lune_packages/option")
|
||||||
|
local Result = require("../../../lune_packages/result")
|
||||||
|
|
||||||
|
local ResultExt = {}
|
||||||
|
|
||||||
|
function ResultExt.ok<T, E>(self: Result.Result<T, E>): Option.Option<T>
|
||||||
|
return self:mapOr(Option.None, function(val: T)
|
||||||
|
return Option.Some(val)
|
||||||
|
end) :: Option.Option<T>
|
||||||
|
end
|
||||||
|
|
||||||
|
return ResultExt
|
|
@ -1,34 +0,0 @@
|
||||||
--> Non-exhaustive set of extensions for Option<->Result conversion
|
|
||||||
|
|
||||||
local OptionImpl = require("../../lune_packages/option")
|
|
||||||
local ResultImpl = require("../../lune_packages/result")
|
|
||||||
|
|
||||||
local Option = {}
|
|
||||||
local Result = {}
|
|
||||||
|
|
||||||
export type Option<T> = OptionImpl.Option<T> & typeof(Option)
|
|
||||||
export type Result<T, E> = ResultImpl.Result<T, E> & typeof(Result)
|
|
||||||
|
|
||||||
function Option.okOr<T, E>(self: Option<T>, err: E): Result<T, E>
|
|
||||||
return self:mapOrElse(function()
|
|
||||||
return ResultImpl.Err(err)
|
|
||||||
end, function(val)
|
|
||||||
return ResultImpl.Ok(val)
|
|
||||||
end) :: Result<T, E>
|
|
||||||
end
|
|
||||||
|
|
||||||
function Result.ok<T, E>(self: Result<T, E>): Option<T>
|
|
||||||
return self:mapOr(OptionImpl.None, function(val: T)
|
|
||||||
return OptionImpl.Some(val)
|
|
||||||
end) :: Option<T>
|
|
||||||
end
|
|
||||||
|
|
||||||
return {
|
|
||||||
Option = setmetatable(OptionImpl, {
|
|
||||||
__index = Option,
|
|
||||||
}),
|
|
||||||
|
|
||||||
Result = setmetatable(ResultImpl, {
|
|
||||||
__index = Result,
|
|
||||||
}),
|
|
||||||
}
|
|
Loading…
Reference in a new issue