aboutsummaryrefslogtreecommitdiff
path: root/src/zen/zen.cpp
diff options
context:
space:
mode:
authorDan Engelbrecht <[email protected]>2025-11-07 11:22:45 +0100
committerGitHub Enterprise <[email protected]>2025-11-07 11:22:45 +0100
commitfa2b84a47a305bc3364eb71194b347d981e3c9fe (patch)
treee0e3ae5a3c3b22e14dc68bcb08433e6b67ebaa54 /src/zen/zen.cpp
parentremotestore op refactorings (#637) (diff)
downloadarchived-zen-fa2b84a47a305bc3364eb71194b347d981e3c9fe.tar.xz
archived-zen-fa2b84a47a305bc3364eb71194b347d981e3c9fe.zip
move progress bar to separate file (#638)
* move progress bar to separate file
Diffstat (limited to 'src/zen/zen.cpp')
-rw-r--r--src/zen/zen.cpp360
1 files changed, 2 insertions, 358 deletions
diff --git a/src/zen/zen.cpp b/src/zen/zen.cpp
index 2661f2173..c47dc642f 100644
--- a/src/zen/zen.cpp
+++ b/src/zen/zen.cpp
@@ -50,6 +50,8 @@
#include <zencore/memory/memorytrace.h>
#include <zencore/memory/newdelete.h>
+#include "progressbar.h"
+
#if ZEN_WITH_TESTS
# define ZEN_TEST_WITH_RUNNER 1
# include <zencore/testing.h>
@@ -70,88 +72,6 @@ ZEN_THIRD_PARTY_INCLUDES_END
namespace zen {
-#if ZEN_PLATFORM_WINDOWS
-static HANDLE
-GetConsoleHandle()
-{
- static HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
- return hStdOut;
-}
-#endif
-
-static bool
-CheckStdoutTty()
-{
-#if ZEN_PLATFORM_WINDOWS
- HANDLE hStdOut = GetConsoleHandle();
- DWORD dwMode = 0;
- static bool IsConsole = ::GetConsoleMode(hStdOut, &dwMode);
- return IsConsole;
-#else
- return isatty(fileno(stdout));
-#endif
-}
-
-static bool
-IsStdoutTty()
-{
- static bool StdoutIsTty = CheckStdoutTty();
- return StdoutIsTty;
-}
-
-static void
-OutputToConsoleRaw(const char* String, size_t Length)
-{
-#if ZEN_PLATFORM_WINDOWS
- HANDLE hStdOut = GetConsoleHandle();
-#endif
-
-#if ZEN_PLATFORM_WINDOWS
- if (IsStdoutTty())
- {
- WriteConsoleA(hStdOut, String, (DWORD)Length, 0, 0);
- }
- else
- {
- ::WriteFile(hStdOut, (LPCVOID)String, (DWORD)Length, 0, 0);
- }
-#else
- fwrite(String, 1, Length, stdout);
-#endif
-}
-
-static void
-OutputToConsoleRaw(const std::string& String)
-{
- OutputToConsoleRaw(String.c_str(), String.length());
-}
-
-static void
-OutputToConsoleRaw(const StringBuilderBase& SB)
-{
- OutputToConsoleRaw(SB.c_str(), SB.Size());
-}
-
-static uint32_t
-GetConsoleColumns(uint32_t Default)
-{
-#if ZEN_PLATFORM_WINDOWS
- HANDLE hStdOut = GetConsoleHandle();
- CONSOLE_SCREEN_BUFFER_INFO csbi;
- if (GetConsoleScreenBufferInfo(hStdOut, &csbi) == TRUE)
- {
- return (uint32_t)(csbi.srWindow.Right - csbi.srWindow.Left + 1);
- }
-#else
- struct winsize w;
- if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) == 0)
- {
- return (uint32_t)w.ws_col;
- }
-#endif
- return Default;
-}
-
enum class ReturnCode : std::int8_t
{
kSuccess = 0,
@@ -367,282 +287,6 @@ ZenCmdBase::LogExecutableVersionAndPid()
ZEN_CONSOLE("Running {}: {} (pid {})", GetRunningExecutablePath(), ZEN_CFG_VERSION_BUILD_STRING_FULL, GetCurrentProcessId());
}
-void
-ProgressBar::SetLogOperationName(Mode InMode, std::string_view Name)
-{
- ZEN_ASSERT(Name.find('\"') == std::string_view::npos);
- if (InMode == Mode::Log)
- {
- std::string String = fmt::format("@progress \"{}\"\n", Name);
- OutputToConsoleRaw(String);
- }
-}
-
-void
-ProgressBar::SetLogOperationProgress(Mode InMode, uint32_t StepIndex, uint32_t StepCount)
-{
- if (InMode == Mode::Log)
- {
- const size_t PercentDone = StepCount > 0u ? gsl::narrow<uint8_t>((100 * StepIndex) / StepCount) : 0u;
-
- std::string String = fmt::format("@progress {}%\n", PercentDone);
- OutputToConsoleRaw(String);
- }
-}
-
-void
-ProgressBar::PushLogOperation(Mode InMode, std::string_view Name)
-{
- if (InMode == Mode::Log)
- {
- std::string String = fmt::format("@progress push \"{}\"\n", Name);
- OutputToConsoleRaw(String);
- }
-}
-
-void
-ProgressBar::PopLogOperation(Mode InMode)
-{
- if (InMode == Mode::Log)
- {
- const std::string String("@progress pop\n");
- OutputToConsoleRaw(String);
- }
-}
-
-ProgressBar::ProgressBar(Mode InMode, std::string_view InSubTask)
-: m_Mode((!IsStdoutTty() && InMode == Mode::Pretty) ? Mode::Plain : InMode)
-, m_LastUpdateMS((uint64_t)-1)
-, m_PausedMS(0)
-, m_SubTask(InSubTask)
-{
- ZEN_ASSERT(InSubTask.find('\"') == std::string_view::npos);
- if (!m_SubTask.empty())
- {
- PushLogOperation(InMode, m_SubTask);
- }
-}
-
-ProgressBar::~ProgressBar()
-{
- try
- {
- ForceLinebreak();
- if (!m_SubTask.empty())
- {
- PopLogOperation(m_Mode);
- }
- }
- catch (const std::exception& Ex)
- {
- ZEN_ERROR("ProgressBar::~ProgressBar() failed with {}", Ex.what());
- }
-}
-
-void
-ProgressBar::UpdateState(const State& NewState, bool DoLinebreak)
-{
- ZEN_ASSERT(NewState.TotalCount >= NewState.RemainingCount);
- ZEN_ASSERT(NewState.Task.find('\"') == std::string::npos);
- if (DoLinebreak == false && m_State == NewState)
- {
- return;
- }
-
- uint64_t ElapsedTimeMS = m_SW.GetElapsedTimeMs();
- if (m_LastUpdateMS != (uint64_t)-1)
- {
- if (!DoLinebreak && (NewState.Status == m_State.Status) && (NewState.Task == m_State.Task) &&
- ((m_LastUpdateMS + 200) > ElapsedTimeMS))
- {
- return;
- }
- if (m_State.Status == State::EStatus::Paused)
- {
- uint64_t ElapsedSinceLast = ElapsedTimeMS - m_LastUpdateMS;
- m_PausedMS += ElapsedSinceLast;
- }
- }
-
- m_LastUpdateMS = ElapsedTimeMS;
-
- std::string Task = NewState.Task;
- switch (NewState.Status)
- {
- case State::EStatus::Aborted:
- Task = "Aborting";
- break;
- case State::EStatus::Paused:
- Task = "Paused";
- break;
- default:
- break;
- }
- if (NewState.Task.length() > Task.length())
- {
- Task += std::string(NewState.Task.length() - Task.length(), ' ');
- }
-
- const size_t PercentDone =
- NewState.TotalCount > 0u ? gsl::narrow<uint8_t>((100 * (NewState.TotalCount - NewState.RemainingCount)) / NewState.TotalCount) : 0u;
-
- if (m_Mode == Mode::Plain)
- {
- const std::string Details = (!NewState.Details.empty()) ? fmt::format(": {}", NewState.Details) : "";
- const std::string Output = fmt::format("{} {}% ({}){}\n", Task, PercentDone, NiceTimeSpanMs(ElapsedTimeMS), Details);
- OutputToConsoleRaw(Output);
- }
- else if (m_Mode == Mode::Pretty)
- {
- size_t ProgressBarSize = 20;
-
- size_t ProgressBarCount = (ProgressBarSize * PercentDone) / 100;
- uint64_t Completed = NewState.TotalCount - NewState.RemainingCount;
- uint64_t ETAElapsedMS = ElapsedTimeMS -= m_PausedMS;
- uint64_t ETAMS =
- (NewState.Status == State::EStatus::Running) && (PercentDone > 5) ? (ETAElapsedMS * NewState.RemainingCount) / Completed : 0;
-
- uint32_t ConsoleColumns = GetConsoleColumns(1024);
-
- const std::string PercentString = fmt::format("{:#3}%", PercentDone);
-
- const std::string ProgressBarString =
- fmt::format(": |{}{}|", std::string(ProgressBarCount, '#'), std::string(ProgressBarSize - ProgressBarCount, ' '));
-
- const std::string ElapsedString = fmt::format(": {}", NiceTimeSpanMs(ElapsedTimeMS));
-
- const std::string ETAString = (ETAMS > 0) ? fmt::format(" ETA {}", NiceTimeSpanMs(ETAMS)) : "";
-
- const std::string DetailsString = (!NewState.Details.empty()) ? fmt::format(". {}", NewState.Details) : "";
-
- ExtendableStringBuilder<256> OutputBuilder;
-
- OutputBuilder << "\r" << Task << " " << PercentString;
- if (OutputBuilder.Size() + 1 < ConsoleColumns)
- {
- size_t RemainingSpace = ConsoleColumns - (OutputBuilder.Size() + 1);
- bool ElapsedFits = RemainingSpace >= ElapsedString.length();
- RemainingSpace -= ElapsedString.length();
- bool ETAFits = ElapsedFits && RemainingSpace >= ETAString.length();
- RemainingSpace -= ETAString.length();
- bool DetailsFits = ETAFits && RemainingSpace >= DetailsString.length();
- RemainingSpace -= DetailsString.length();
- bool ProgressBarFits = DetailsFits && RemainingSpace >= ProgressBarString.length();
- RemainingSpace -= ProgressBarString.length();
-
- if (ProgressBarFits)
- {
- OutputBuilder << ProgressBarString;
- }
- if (ElapsedFits)
- {
- OutputBuilder << ElapsedString;
- }
- if (ETAFits)
- {
- OutputBuilder << ETAString;
- }
- if (DetailsFits)
- {
- OutputBuilder << DetailsString;
- }
- }
-
- std::string_view Output = OutputBuilder.ToView();
- std::string::size_type EraseLength = m_LastOutputLength > Output.length() ? (m_LastOutputLength - Output.length()) : 0;
-
- ExtendableStringBuilder<256> LineToPrint;
-
- if (Output.length() + EraseLength >= ConsoleColumns)
- {
- if (m_LastOutputLength > 0)
- {
- LineToPrint << "\n";
- }
- LineToPrint << Output.substr(1);
- DoLinebreak = true;
- }
- else
- {
- LineToPrint << Output << std::string(EraseLength, ' ');
- }
-
- if (DoLinebreak)
- {
- LineToPrint << "\n";
- }
-
- OutputToConsoleRaw(LineToPrint);
-
- m_LastOutputLength = DoLinebreak ? 0 : Output.length();
- m_State = NewState;
- }
- else if (m_Mode == Mode::Log)
- {
- if (m_State.Task != NewState.Task ||
- m_State.Details != NewState.Details) // TODO: Should we output just because details change? Will this spam the log collector?
- {
- std::string Details = (!NewState.Details.empty()) ? fmt::format(": {}", NewState.Details) : "";
- for (std::string::value_type& Char : Details)
- {
- if (Char == '"')
- {
- Char = '\'';
- }
- }
- const std::string Message = fmt::format("@progress \"{} ({}){}\"\n", NewState.Task, NiceTimeSpanMs(ElapsedTimeMS), Details);
- OutputToConsoleRaw(Message);
- }
-
- const size_t OldPercentDone =
- m_State.TotalCount > 0u ? gsl::narrow<uint8_t>((100 * (m_State.TotalCount - m_State.RemainingCount)) / m_State.TotalCount) : 0u;
-
- if (OldPercentDone != PercentDone)
- {
- const std::string Progress = fmt::format("@progress {}%\n", PercentDone);
- OutputToConsoleRaw(Progress);
- }
- m_State = NewState;
- }
-}
-
-void
-ProgressBar::ForceLinebreak()
-{
- if (m_LastOutputLength > 0)
- {
- State NewState = m_State;
- UpdateState(NewState, /*DoLinebreak*/ true);
- }
-}
-
-void
-ProgressBar::Finish()
-{
- if (m_LastOutputLength > 0 || m_State.RemainingCount > 0)
- {
- State NewState = m_State;
- NewState.RemainingCount = 0;
- NewState.Details = "";
- UpdateState(NewState, /*DoLinebreak*/ true);
- }
- m_State = State{};
- m_LastOutputLength = 0;
- m_SW.Reset();
-}
-
-bool
-ProgressBar::IsSameTask(std::string_view Task) const
-{
- return Task == m_State.Task;
-}
-
-bool
-ProgressBar::HasActiveTask() const
-{
- return !m_State.Task.empty();
-}
-
} // namespace zen
//////////////////////////////////////////////////////////////////////////