generated from marked/CSSharpTemplate
135 lines
4.5 KiB
Text
135 lines
4.5 KiB
Text
--!strict
|
|
--> Inspired by Rokit's progress bar: https://github.com/rojo-rbx/rokit/blob/a303faf/src/util/progress.rs
|
|
-- Original: https://github.com/pesde-pkg/tooling/blob/main/toolchainlib/src/utils/progress.luau
|
|
--[[
|
|
MIT License
|
|
|
|
Copyright (c) 2024 pesde-pkg
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
of this software and associated documentation files (the "Software"), to deal
|
|
in the Software without restriction, including without limitation the rights
|
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
copies of the Software, and to permit persons to whom the Software is
|
|
furnished to do so, subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included in all
|
|
copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
SOFTWARE.
|
|
]]
|
|
local stdio = require("@lune/stdio")
|
|
local task = require("@lune/task")
|
|
|
|
local result = require("./result")
|
|
|
|
-- FORMAT: {SPINNER} {MESSAGE} {BAR} {STAGE}
|
|
local SPINNERS = { "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏" }
|
|
local BAR_COMPONENT = "▇"
|
|
local MAX_BAR_LENGTH = 30
|
|
|
|
local ProgressBar = {}
|
|
type ProgressBar = {
|
|
stages: { { tag: string, message: string } },
|
|
current_stage_index: number,
|
|
finished: boolean,
|
|
paused: boolean,
|
|
thread: thread?,
|
|
}
|
|
export type ProgressBarImpl = typeof(setmetatable({} :: ProgressBar, { __index = ProgressBar }))
|
|
|
|
function ProgressBar.new(): ProgressBarImpl
|
|
return setmetatable(
|
|
{
|
|
stages = {},
|
|
current_stage_index = 1,
|
|
finished = false,
|
|
} :: ProgressBar,
|
|
{
|
|
__index = ProgressBar,
|
|
}
|
|
)
|
|
end
|
|
|
|
function ProgressBar.with_stage(self: ProgressBarImpl, tag: string, msg: string): ProgressBarImpl
|
|
table.insert(self.stages, { tag = tag, message = msg })
|
|
return self
|
|
end
|
|
|
|
function ProgressBar.start(self: ProgressBarImpl)
|
|
local BAR_LENGTH = MAX_BAR_LENGTH // #self.stages
|
|
local TOTAL_BAR_LENGTH = BAR_LENGTH * #self.stages
|
|
local BAR = string.rep(BAR_COMPONENT, BAR_LENGTH)
|
|
local MAX_MESSAGE_LENGTH = 0
|
|
for _, stage in self.stages do
|
|
local len = #stage.message
|
|
if len > MAX_MESSAGE_LENGTH then
|
|
MAX_MESSAGE_LENGTH = len
|
|
end
|
|
end
|
|
|
|
self.thread = task.spawn(function()
|
|
while true do
|
|
if self.paused then
|
|
task.wait(0.1)
|
|
continue
|
|
end
|
|
|
|
for _, spinner in SPINNERS do
|
|
if self.finished then
|
|
return
|
|
end
|
|
|
|
local stage = self.stages[self.current_stage_index]
|
|
stdio.ewrite(
|
|
`\x1b[2K\x1b[0G{stdio.color("cyan")}{spinner} {stage.message}{stdio.color("reset")}{string.rep(
|
|
" ",
|
|
MAX_MESSAGE_LENGTH - #stage.message
|
|
)} [{stdio.style("dim")}{string.rep(BAR, self.current_stage_index)}{string.rep(
|
|
" ",
|
|
TOTAL_BAR_LENGTH - (BAR_LENGTH * self.current_stage_index)
|
|
)}{stdio.style("reset")}] {stdio.style("bold")}{self.current_stage_index} / {#self.stages}{stdio.style(
|
|
"reset"
|
|
)}`
|
|
)
|
|
|
|
task.wait(0.1)
|
|
end
|
|
end
|
|
end)
|
|
end
|
|
|
|
function ProgressBar.stop(self: ProgressBarImpl)
|
|
-- Trigger upvalue, kill thread and clean progress bar remnant
|
|
self.finished = true
|
|
stdio.ewrite("\x1b[2K\x1b[0G")
|
|
end
|
|
|
|
function ProgressBar.pause(self: ProgressBarImpl)
|
|
self.paused = true
|
|
stdio.ewrite("\x1b[2K\x1b[0G")
|
|
end
|
|
|
|
function ProgressBar.unpause(self: ProgressBarImpl)
|
|
self.paused = false
|
|
end
|
|
|
|
function ProgressBar.next_stage(self: ProgressBarImpl): result.Identity<nil>
|
|
local inc = self.current_stage_index + 1
|
|
if inc > #self.stages then
|
|
-- TODO: Make this a result
|
|
self.finished = true
|
|
return result(false, "OutOfBounds - Attempted to advance past last stage")
|
|
end
|
|
|
|
self.current_stage_index = inc
|
|
return result(true, nil)
|
|
end
|
|
|
|
return ProgressBar
|