From f5b6b4d77978805231c0dca3fcf511dd4cd46980 Mon Sep 17 00:00:00 2001 From: Pranav Srinivas Kumar Date: Thu, 13 Feb 2020 14:20:40 +0530 Subject: [PATCH] Added single header file v1.7 --- README.md | 12 +- clang-format.bash | 2 +- single_include/indicators/indicators.hpp | 1427 ++++++++++++++++++++++ 3 files changed, 1434 insertions(+), 7 deletions(-) create mode 100644 single_include/indicators/indicators.hpp diff --git a/README.md b/README.md index 556281f..23f5adb 100644 --- a/README.md +++ b/README.md @@ -70,8 +70,8 @@ int main() { option::Lead{">"}, option::Remainder{" "}, option::End{"]"}, - option::PostfixText{"Extracting Archive"}; - option::ForegroundColor{Color::green}; + option::PostfixText{"Extracting Archive"}, + option::ForegroundColor{Color::green} }; // Update bar state @@ -174,9 +174,9 @@ int main() { option::Remainder{"-"}, option::End{"]"}, option::PrefixText{"Training Gaze Network 👀"}, - option::ForegroundColor{Color::yellow} - option::ShowElapsedTime{true}; - option::ShowRemainingTime{true}; + option::ForegroundColor{Color::yellow}, + option::ShowElapsedTime{true}, + option::ShowRemainingTime{true} }; // Update bar state @@ -271,7 +271,7 @@ int main() { // Configure second progress bar - ProgressBar bar1{ + ProgressBar bar2{ option::BarWidth{50}, option::Start{"["}, option::Fill{"="}, diff --git a/clang-format.bash b/clang-format.bash index 49b6ac6..07a71cf 100755 --- a/clang-format.bash +++ b/clang-format.bash @@ -1,2 +1,2 @@ #!/usr/bin/env bash -find ./include ./demo/ ./samples/ -type f \( -iname \*.cpp -o -iname \*.hpp \) | xargs clang-format -style="{ColumnLimit : 100}" -i +find ./include ./demo/ ./samples/ ./single_include -type f \( -iname \*.cpp -o -iname \*.hpp \) | xargs clang-format -style="{ColumnLimit : 100}" -i diff --git a/single_include/indicators/indicators.hpp b/single_include/indicators/indicators.hpp new file mode 100644 index 0000000..514f127 --- /dev/null +++ b/single_include/indicators/indicators.hpp @@ -0,0 +1,1427 @@ +/* +Activity Indicators for Modern C++ +https://github.com/p-ranav/indica + +Licensed under the MIT License . +SPDX-License-Identifier: MIT +Copyright (c) 2019 Pranav Srinivas Kumar . + +Setting class written by Dawid Pilarski +Licensed under the MIT License . +SPDX-License-Identifier: MIT +Copyright (c) 2019 Dawid Pilarski . + +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. +*/ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#define NOMINMAX +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace indicators { +enum class Color { grey, red, green, yellow, blue, magenta, cyan, white }; +} + +//! +//! termcolor +//! ~~~~~~~~~ +//! +//! termcolor is a header-only c++ library for printing colored messages +//! to the terminal. Written just for fun with a help of the Force. +//! +//! :copyright: (c) 2013 by Ihor Kalnytskyi +//! :license: BSD, see LICENSE for details +//! + +#ifndef TERMCOLOR_HPP_ +#define TERMCOLOR_HPP_ + +// the following snippet of code detects the current OS and +// defines the appropriate macro that is used to wrap some +// platform specific things +#if defined(_WIN32) || defined(_WIN64) +#define TERMCOLOR_OS_WINDOWS +#elif defined(__APPLE__) +#define TERMCOLOR_OS_MACOS +#elif defined(__unix__) || defined(__unix) +#define TERMCOLOR_OS_LINUX +#else +#error unsupported platform +#endif + +// This headers provides the `isatty()`/`fileno()` functions, +// which are used for testing whether a standart stream refers +// to the terminal. As for Windows, we also need WinApi funcs +// for changing colors attributes of the terminal. +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) +#include +#elif defined(TERMCOLOR_OS_WINDOWS) +#include +#include +#endif + +#include +#include + +namespace termcolor { +// Forward declaration of the `_internal` namespace. +// All comments are below. +namespace _internal { +// An index to be used to access a private storage of I/O streams. See +// colorize / nocolorize I/O manipulators for details. +static int colorize_index = std::ios_base::xalloc(); + +inline FILE *get_standard_stream(const std::ostream &stream); +inline bool is_colorized(std::ostream &stream); +inline bool is_atty(const std::ostream &stream); + +#if defined(TERMCOLOR_OS_WINDOWS) +inline void win_change_attributes(std::ostream &stream, int foreground, int background = -1); +#endif +} // namespace _internal + +inline std::ostream &colorize(std::ostream &stream) { + stream.iword(_internal::colorize_index) = 1L; + return stream; +} + +inline std::ostream &nocolorize(std::ostream &stream) { + stream.iword(_internal::colorize_index) = 0L; + return stream; +} + +inline std::ostream &reset(std::ostream &stream) { + if (_internal::is_colorized(stream)) { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\033[00m"; +#elif defined(TERMCOLOR_OS_WINDOWS) + _internal::win_change_attributes(stream, -1, -1); +#endif + } + return stream; +} + +inline std::ostream &bold(std::ostream &stream) { + if (_internal::is_colorized(stream)) { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\033[1m"; +#elif defined(TERMCOLOR_OS_WINDOWS) +#endif + } + return stream; +} + +inline std::ostream &dark(std::ostream &stream) { + if (_internal::is_colorized(stream)) { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\033[2m"; +#elif defined(TERMCOLOR_OS_WINDOWS) +#endif + } + return stream; +} + +inline std::ostream &italic(std::ostream &stream) { + if (_internal::is_colorized(stream)) { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\033[3m"; +#elif defined(TERMCOLOR_OS_WINDOWS) +#endif + } + return stream; +} + +inline std::ostream &underline(std::ostream &stream) { + if (_internal::is_colorized(stream)) { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\033[4m"; +#elif defined(TERMCOLOR_OS_WINDOWS) +#endif + } + return stream; +} + +inline std::ostream &blink(std::ostream &stream) { + if (_internal::is_colorized(stream)) { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\033[5m"; +#elif defined(TERMCOLOR_OS_WINDOWS) +#endif + } + return stream; +} + +inline std::ostream &reverse(std::ostream &stream) { + if (_internal::is_colorized(stream)) { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\033[7m"; +#elif defined(TERMCOLOR_OS_WINDOWS) +#endif + } + return stream; +} + +inline std::ostream &concealed(std::ostream &stream) { + if (_internal::is_colorized(stream)) { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\033[8m"; +#elif defined(TERMCOLOR_OS_WINDOWS) +#endif + } + return stream; +} + +inline std::ostream &crossed(std::ostream &stream) { + if (_internal::is_colorized(stream)) { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\033[9m"; +#elif defined(TERMCOLOR_OS_WINDOWS) +#endif + } + return stream; +} + +inline std::ostream &grey(std::ostream &stream) { + if (_internal::is_colorized(stream)) { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\033[30m"; +#elif defined(TERMCOLOR_OS_WINDOWS) + _internal::win_change_attributes(stream, + 0 // grey (black) + ); +#endif + } + return stream; +} + +inline std::ostream &red(std::ostream &stream) { + if (_internal::is_colorized(stream)) { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\033[31m"; +#elif defined(TERMCOLOR_OS_WINDOWS) + _internal::win_change_attributes(stream, FOREGROUND_RED); +#endif + } + return stream; +} + +inline std::ostream &green(std::ostream &stream) { + if (_internal::is_colorized(stream)) { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\033[32m"; +#elif defined(TERMCOLOR_OS_WINDOWS) + _internal::win_change_attributes(stream, FOREGROUND_GREEN); +#endif + } + return stream; +} + +inline std::ostream &yellow(std::ostream &stream) { + if (_internal::is_colorized(stream)) { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\033[33m"; +#elif defined(TERMCOLOR_OS_WINDOWS) + _internal::win_change_attributes(stream, FOREGROUND_GREEN | FOREGROUND_RED); +#endif + } + return stream; +} + +inline std::ostream &blue(std::ostream &stream) { + if (_internal::is_colorized(stream)) { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\033[34m"; +#elif defined(TERMCOLOR_OS_WINDOWS) + _internal::win_change_attributes(stream, FOREGROUND_BLUE); +#endif + } + return stream; +} + +inline std::ostream &magenta(std::ostream &stream) { + if (_internal::is_colorized(stream)) { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\033[35m"; +#elif defined(TERMCOLOR_OS_WINDOWS) + _internal::win_change_attributes(stream, FOREGROUND_BLUE | FOREGROUND_RED); +#endif + } + return stream; +} + +inline std::ostream &cyan(std::ostream &stream) { + if (_internal::is_colorized(stream)) { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\033[36m"; +#elif defined(TERMCOLOR_OS_WINDOWS) + _internal::win_change_attributes(stream, FOREGROUND_BLUE | FOREGROUND_GREEN); +#endif + } + return stream; +} + +inline std::ostream &white(std::ostream &stream) { + if (_internal::is_colorized(stream)) { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\033[37m"; +#elif defined(TERMCOLOR_OS_WINDOWS) + _internal::win_change_attributes(stream, FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED); +#endif + } + return stream; +} + +inline std::ostream &on_grey(std::ostream &stream) { + if (_internal::is_colorized(stream)) { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\033[40m"; +#elif defined(TERMCOLOR_OS_WINDOWS) + _internal::win_change_attributes(stream, -1, + 0 // grey (black) + ); +#endif + } + return stream; +} + +inline std::ostream &on_red(std::ostream &stream) { + if (_internal::is_colorized(stream)) { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\033[41m"; +#elif defined(TERMCOLOR_OS_WINDOWS) + _internal::win_change_attributes(stream, -1, BACKGROUND_RED); +#endif + } + return stream; +} + +inline std::ostream &on_green(std::ostream &stream) { + if (_internal::is_colorized(stream)) { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\033[42m"; +#elif defined(TERMCOLOR_OS_WINDOWS) + _internal::win_change_attributes(stream, -1, BACKGROUND_GREEN); +#endif + } + return stream; +} + +inline std::ostream &on_yellow(std::ostream &stream) { + if (_internal::is_colorized(stream)) { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\033[43m"; +#elif defined(TERMCOLOR_OS_WINDOWS) + _internal::win_change_attributes(stream, -1, BACKGROUND_GREEN | BACKGROUND_RED); +#endif + } + return stream; +} + +inline std::ostream &on_blue(std::ostream &stream) { + if (_internal::is_colorized(stream)) { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\033[44m"; +#elif defined(TERMCOLOR_OS_WINDOWS) + _internal::win_change_attributes(stream, -1, BACKGROUND_BLUE); +#endif + } + return stream; +} + +inline std::ostream &on_magenta(std::ostream &stream) { + if (_internal::is_colorized(stream)) { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\033[45m"; +#elif defined(TERMCOLOR_OS_WINDOWS) + _internal::win_change_attributes(stream, -1, BACKGROUND_BLUE | BACKGROUND_RED); +#endif + } + return stream; +} + +inline std::ostream &on_cyan(std::ostream &stream) { + if (_internal::is_colorized(stream)) { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\033[46m"; +#elif defined(TERMCOLOR_OS_WINDOWS) + _internal::win_change_attributes(stream, -1, BACKGROUND_GREEN | BACKGROUND_BLUE); +#endif + } + return stream; +} + +inline std::ostream &on_white(std::ostream &stream) { + if (_internal::is_colorized(stream)) { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\033[47m"; +#elif defined(TERMCOLOR_OS_WINDOWS) + _internal::win_change_attributes(stream, -1, + BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_RED); +#endif + } + + return stream; +} + +//! Since C++ hasn't a way to hide something in the header from +//! the outer access, I have to introduce this namespace which +//! is used for internal purpose and should't be access from +//! the user code. +namespace _internal { +//! Since C++ hasn't a true way to extract stream handler +//! from the a given `std::ostream` object, I have to write +//! this kind of hack. +inline FILE *get_standard_stream(const std::ostream &stream) { + if (&stream == &std::cout) + return stdout; + else if ((&stream == &std::cerr) || (&stream == &std::clog)) + return stderr; + + return 0; +} + +// Say whether a given stream should be colorized or not. It's always +// true for ATTY streams and may be true for streams marked with +// colorize flag. +inline bool is_colorized(std::ostream &stream) { + return is_atty(stream) || static_cast(stream.iword(colorize_index)); +} + +//! Test whether a given `std::ostream` object refers to +//! a terminal. +inline bool is_atty(const std::ostream &stream) { + FILE *std_stream = get_standard_stream(stream); + + // Unfortunately, fileno() ends with segmentation fault + // if invalid file descriptor is passed. So we need to + // handle this case gracefully and assume it's not a tty + // if standard stream is not detected, and 0 is returned. + if (!std_stream) + return false; + +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + return ::isatty(fileno(std_stream)); +#elif defined(TERMCOLOR_OS_WINDOWS) + return ::_isatty(_fileno(std_stream)); +#endif +} + +#if defined(TERMCOLOR_OS_WINDOWS) +//! Change Windows Terminal colors attribute. If some +//! parameter is `-1` then attribute won't changed. +inline void win_change_attributes(std::ostream &stream, int foreground, int background) { + // yeah, i know.. it's ugly, it's windows. + static WORD defaultAttributes = 0; + + // Windows doesn't have ANSI escape sequences and so we use special + // API to change Terminal output color. That means we can't + // manipulate colors by means of "std::stringstream" and hence + // should do nothing in this case. + if (!_internal::is_atty(stream)) + return; + + // get terminal handle + HANDLE hTerminal = INVALID_HANDLE_VALUE; + if (&stream == &std::cout) + hTerminal = GetStdHandle(STD_OUTPUT_HANDLE); + else if (&stream == &std::cerr) + hTerminal = GetStdHandle(STD_ERROR_HANDLE); + + // save default terminal attributes if it unsaved + if (!defaultAttributes) { + CONSOLE_SCREEN_BUFFER_INFO info; + if (!GetConsoleScreenBufferInfo(hTerminal, &info)) + return; + defaultAttributes = info.wAttributes; + } + + // restore all default settings + if (foreground == -1 && background == -1) { + SetConsoleTextAttribute(hTerminal, defaultAttributes); + return; + } + + // get current settings + CONSOLE_SCREEN_BUFFER_INFO info; + if (!GetConsoleScreenBufferInfo(hTerminal, &info)) + return; + + if (foreground != -1) { + info.wAttributes &= ~(info.wAttributes & 0x0F); + info.wAttributes |= static_cast(foreground); + } + + if (background != -1) { + info.wAttributes &= ~(info.wAttributes & 0xF0); + info.wAttributes |= static_cast(background); + } + + SetConsoleTextAttribute(hTerminal, info.wAttributes); +} +#endif // TERMCOLOR_OS_WINDOWS + +} // namespace _internal + +} // namespace termcolor + +#undef TERMCOLOR_OS_WINDOWS +#undef TERMCOLOR_OS_MACOS +#undef TERMCOLOR_OS_LINUX + +#endif // TERMCOLOR_HPP_ + + +namespace indicators { +namespace details { + +inline void set_stream_color(std::ostream &os, Color color) { + switch (color) { + case Color::grey: + os << termcolor::grey; + break; + case Color::red: + os << termcolor::red; + break; + case Color::green: + os << termcolor::green; + break; + case Color::yellow: + os << termcolor::yellow; + break; + case Color::blue: + os << termcolor::blue; + break; + case Color::magenta: + os << termcolor::magenta; + break; + case Color::cyan: + os << termcolor::cyan; + break; + case Color::white: + os << termcolor::white; + break; + default: + assert(false); + } +} + +inline std::ostream &write_duration(std::ostream &os, std::chrono::nanoseconds ns) { + using namespace std; + using namespace std::chrono; + using days = duration>; + char fill = os.fill(); + os.fill('0'); + auto d = duration_cast(ns); + ns -= d; + auto h = duration_cast(ns); + ns -= h; + auto m = duration_cast(ns); + ns -= m; + auto s = duration_cast(ns); + if (d.count() > 0) + os << setw(2) << d.count() << "d:"; + if (h.count() > 0) + os << setw(2) << h.count() << "h:"; + os << setw(2) << m.count() << "m:" << setw(2) << s.count() << 's'; + os.fill(fill); + return os; +} + +class BlockProgressScaleWriter +{ +public: + BlockProgressScaleWriter(std::ostream& os, size_t bar_width) + : os(os) + , bar_width(bar_width) + {} + + std::ostream &write(float progress) { + std::string fill_text{"█"}; + std::vector lead_characters{" ", "▏", "▎", "▍", "▌", "▋", "▊", "▉"}; + auto value = std::min(1.0f, std::max(0.0f, progress / 100.0f)); + auto whole_width = std::floor(value * bar_width); + auto remainder_width = fmod((value * bar_width), 1.0f); + auto part_width = std::floor(remainder_width * lead_characters.size()); + std::string lead_text = lead_characters[size_t(part_width)]; + if ((bar_width - whole_width - 1) < 0) + lead_text = ""; + for (size_t i = 0; i < whole_width; ++i) + os << fill_text; + os << lead_text; + for (size_t i = 0; i < (bar_width - whole_width - 1); ++i) + os << " "; + return os; + } +private: + std::ostream& os; + size_t bar_width = 0; +}; + +class ProgressScaleWriter +{ +public: + ProgressScaleWriter(std::ostream& os, + size_t bar_width, + const std::string& fill, + const std::string& lead, + const std::string& remainder) + : os(os) + , bar_width(bar_width) + , fill(fill) + , lead(lead) + , remainder(remainder) + {} + + std::ostream &write(float progress) { + auto pos = static_cast(progress * static_cast(bar_width) / 100.0); + for (size_t i = 0; i < bar_width; ++i) { + if (i < pos) + os << fill; + else if (i == pos) + os << lead; + else + os << remainder; + } + return os; + } + +private: + std::ostream& os; + size_t bar_width = 0; + std::string fill; + std::string lead; + std::string remainder; +}; + +} +} + +namespace indicators{ + +namespace details{ + +template +struct if_else; + +template<> +struct if_else{ + using type = std::true_type; +}; + +template<> +struct if_else{ + using type = std::false_type ; +}; + +template +struct if_else_type; + +template +struct if_else_type{ + using type = True; +}; + +template +struct if_else_type{ + using type = False; +}; + +template +struct conjuction; + +template <> +struct conjuction<> : std::true_type {}; + +template +struct conjuction : if_else_type>::type {}; + +template +struct disjunction; + +template <> +struct disjunction<> : std::false_type {}; + +template +struct disjunction : if_else_type>::type {}; + +enum class ProgressBarOption{ + bar_width=0, + prefix_text, + postfix_text, + start, + end, + fill, + lead, + remainder, + max_postfix_text_len, + completed, + show_percentage, + show_elapsed_time, + show_remaining_time, + saved_start_time, + foreground_color, + spinner_show, + spinner_states +}; + +template +struct Setting{ + template ::value>::type> + explicit Setting(Args&&... args) : value(std::forward(args)...){} + Setting(const Setting&) = default; + Setting(Setting&&) = default; + + static constexpr auto id = Id; + using type = T; + + T value{}; +}; + +template +struct is_setting : std::false_type{}; + +template +struct is_setting> : std::true_type{}; + +template +struct are_settings : if_else...>::value>::type {}; + +template <> +struct are_settings<> : std::true_type{}; + +template +struct is_setting_from_tuple; + +template +struct is_setting_from_tuple> : std::true_type {}; + +template +struct is_setting_from_tuple> : + if_else...>::value>::type {}; + +template +struct are_settings_from_tuple : if_else...>::value>::type {}; + + +template +struct always_true{ + static constexpr auto value = true; +}; + +template +Default&& get_impl(Default&& def){ + return std::forward(def); +} + +template +auto get_impl(Default&& def, T&& first, Args&&... tail) -> typename std::enable_if< + (std::decay::type::id == Id), + decltype(std::forward(first))> + ::type{ + return std::forward(first); +} + +template +auto get_impl(Default&& def, T&& first, Args&&... tail) -> typename std::enable_if< + (std::decay::type::id != Id), + decltype(get_impl(std::forward(def), std::forward(tail)...))>::type{ + return get_impl(std::forward(def), std::forward(tail)...); +} + +template ::value, void>::type> +auto get(Default&& def, Args&&... args) -> decltype(details::get_impl(std::forward(def), std::forward(args)...)){ + return details::get_impl(std::forward(def), std::forward(args)...); +} + +template +using StringSetting = Setting; + +template +using IntegerSetting = Setting; + +template +using BooleanSetting = Setting; + +template +struct option_idx; + +template +struct option_idx, counter> : if_else_type<(Id == T::id), + std::integral_constant, + option_idx, counter+1>>::type{}; + +template +struct option_idx, counter>{ + static_assert(always_true<(ProgressBarOption)Id>::value, "No such option was found"); +}; + +template +auto get_value(Settings&& settings) -> decltype((std::get::type>::value>(std::declval()))){ + return std::get::type>::value>(std::forward(settings)); +} + +} + + +namespace option{ + using BarWidth = details::IntegerSetting; + using PrefixText = details::StringSetting; + using PostfixText = details::StringSetting; + using Start = details::StringSetting; + using End = details::StringSetting; + using Fill = details::StringSetting; + using Lead = details::StringSetting; + using Remainder = details::StringSetting; + using MaxPostfixTextLen = details::IntegerSetting; + using Completed = details::BooleanSetting; + using ShowPercentage = details::BooleanSetting; + using ShowElapsedTime = details::BooleanSetting; + using ShowRemainingTime = details::BooleanSetting; + using SavedStartTime = details::BooleanSetting; + using ForegroundColor = details::Setting; + using ShowSpinner = details::BooleanSetting; + using SpinnerStates = details::Setting, details::ProgressBarOption::spinner_states>; +} +} + +namespace indicators { + +class ProgressBar { + using Settings = std::tuple< + option::BarWidth, + option::PrefixText, + option::PostfixText, + option::Start, + option::End, + option::Fill, + option::Lead, + option::Remainder, + option::MaxPostfixTextLen, + option::Completed, + option::ShowPercentage, + option::ShowElapsedTime, + option::ShowRemainingTime, + option::SavedStartTime, + option::ForegroundColor + >; +public: + template ::type...>::value, void*>::type = nullptr> + explicit ProgressBar(Args&&... args) : + settings_( + details::get(option::BarWidth{100}, std::forward(args)...), + details::get(option::PrefixText{}, std::forward(args)...), + details::get(option::PostfixText{}, std::forward(args)...), + details::get(option::Start{"["}, std::forward(args)...), + details::get(option::End{"]"}, std::forward(args)...), + details::get(option::Fill{"="}, std::forward(args)...), + details::get(option::Lead{">"}, std::forward(args)...), + details::get(option::Remainder{" "}, std::forward(args)...), + details::get(option::MaxPostfixTextLen{0}, std::forward(args)...), + details::get(option::Completed{false}, std::forward(args)...), + details::get(option::ShowPercentage{false} ,std::forward(args)...), + details::get(option::ShowElapsedTime{false}, std::forward(args)...), + details::get(option::ShowRemainingTime{false}, std::forward(args)...), + details::get(option::SavedStartTime{false}, std::forward(args)...), + details::get(option::ForegroundColor{Color::white}, std::forward(args)...) + ) + {} + + template + void set_option(details::Setting&& setting){ + static_assert(!std::is_same(std::declval()))>::type>::value, "Setting has wrong type!"); + std::lock_guard lock(mutex_); + get_value() = std::move(setting).value; + } + + template + void set_option(const details::Setting& setting){ + static_assert(!std::is_same(std::declval()))>::type>::value, "Setting has wrong type!"); + std::lock_guard lock(mutex_); + get_value() = setting.value; + } + + void set_option(const details::Setting& setting){ + std::lock_guard lock(mutex_); + get_value() = setting.value; + if(setting.value.length() > get_value()){ + get_value() = setting.value.length(); + } + } + + void set_option(details::Setting&& setting){ + std::lock_guard lock(mutex_); + get_value() = std::move(setting).value; + auto& new_value = get_value(); + if(new_value.length() > get_value()){ + get_value() = new_value.length(); + } + } + + void set_progress(float new_progress){ + { + std::lock_guard lck(mutex_); + progress_ = new_progress; + } + + save_start_time(); + print_progress(); + } + + void tick() { + { + std::lock_guard lock{mutex_}; + progress_ += 1; + } + save_start_time(); + print_progress(); + } + + size_t current() { + std::lock_guard lock{mutex_}; + return std::min(static_cast(progress_), size_t(100)); + } + + bool is_completed() const { return get_value(); } + + void mark_as_completed() { + get_value() = true; + print_progress(); + } + +private: + + template + auto get_value() -> decltype((details::get_value(std::declval()).value)) { + return details::get_value(settings_).value; + } + + template + auto get_value() const -> decltype((details::get_value(std::declval()).value)) { + return details::get_value(settings_).value; + } + + float progress_{0}; + Settings settings_; + std::chrono::nanoseconds elapsed_; + std::chrono::time_point start_time_point_; + std::mutex mutex_; + + template friend class MultiProgress; + std::atomic multi_progress_mode_{false}; + + void save_start_time() { + auto& show_elapsed_time = get_value(); + auto& saved_start_time = get_value(); + auto& show_remaining_time = get_value(); + if ((show_elapsed_time || show_remaining_time) && !saved_start_time) { + start_time_point_ = std::chrono::high_resolution_clock::now(); + saved_start_time = true; + } + } + + void print_progress(bool from_multi_progress = false) { + if (multi_progress_mode_ && !from_multi_progress) { + if (progress_ > 100.0) { + get_value() = true; + } + return; + } + std::lock_guard lock{mutex_}; + auto now = std::chrono::high_resolution_clock::now(); + if (!get_value()) + elapsed_ = std::chrono::duration_cast(now - start_time_point_); + + std::cout << termcolor::bold; + details::set_stream_color(std::cout, get_value()); + std::cout << get_value(); + + std::cout << get_value(); + + details::ProgressScaleWriter writer{std::cout, get_value(), + get_value(), + get_value(), + get_value()}; + writer.write(progress_); + + std::cout << get_value(); + + if (get_value()) { + std::cout << " " << std::min(static_cast(progress_), size_t(100)) << "%"; + } + + if (get_value()) { + std::cout << " ["; + details::write_duration(std::cout, elapsed_); + } + + if (get_value()) { + if (get_value()) + std::cout << "<"; + else + std::cout << " ["; + auto eta = std::chrono::nanoseconds( + progress_ > 0 ? static_cast(elapsed_.count() * 100 / progress_) : 0); + auto remaining = eta > elapsed_ ? (eta - elapsed_) : (elapsed_ - eta); + details::write_duration(std::cout, remaining); + std::cout << "]"; + } else { + if (get_value()) + std::cout << "]"; + } + + if (get_value() == 0) + get_value() = 10; + std::cout << " " << get_value() << std::string(get_value(), ' ') << "\r"; + std::cout.flush(); + if (progress_ > 100.0) { + get_value() = true; + } + if (get_value() && !from_multi_progress) // Don't std::endl if calling from MultiProgress + std::cout << termcolor::reset << std::endl; + } +}; + +} // namespace indicators + +namespace indicators { + +class BlockProgressBar { + using Settings = std::tuple< + option::ForegroundColor, + option::BarWidth, + option::Start, + option::End, + option::PrefixText, + option::PostfixText, + option::ShowPercentage, + option::ShowElapsedTime, + option::ShowRemainingTime, + option::Completed, + option::SavedStartTime, + option::MaxPostfixTextLen>; +public: + template ::type...>::value, void*>::type = nullptr> + explicit BlockProgressBar(Args&&... args) : settings_( + details::get(option::ForegroundColor{Color::white}, std::forward(args)...), + details::get(option::BarWidth{100}, std::forward(args)...), + details::get(option::Start{"["}, std::forward(args)...), + details::get(option::End{"]"}, std::forward(args)...), + details::get(option::PrefixText{""}, std::forward(args)...), + details::get(option::PostfixText{""}, std::forward(args)...), + details::get(option::ShowPercentage{true}, std::forward(args)...), + details::get(option::ShowElapsedTime{false}, std::forward(args)...), + details::get(option::ShowRemainingTime{false}, std::forward(args)...), + details::get(option::Completed{false}, std::forward(args)...), + details::get(option::SavedStartTime{false}, std::forward(args)...), + details::get(option::MaxPostfixTextLen{0}, std::forward(args)...) + ) {} + + template + void set_option(details::Setting&& setting){ + static_assert(!std::is_same(std::declval()))>::type>::value, "Setting has wrong type!"); + std::lock_guard lock(mutex_); + get_value() = std::move(setting).value; + } + + template + void set_option(const details::Setting& setting){ + static_assert(!std::is_same(std::declval()))>::type>::value, "Setting has wrong type!"); + std::lock_guard lock(mutex_); + get_value() = setting.value; + } + + void set_option(const details::Setting& setting){ + std::lock_guard lock(mutex_); + get_value() = setting.value; + if(setting.value.length() > get_value()){ + get_value() = setting.value.length(); + } + } + + void set_option(details::Setting&& setting){ + std::lock_guard lock(mutex_); + get_value() = std::move(setting).value; + auto& new_value = get_value(); + if(new_value.length() > get_value()){ + get_value() = new_value.length(); + } + } + + void set_progress(float value) { + { + std::lock_guard lock{mutex_}; + progress_ = value; + } + save_start_time(); + print_progress(); + } + + void tick() { + { + std::lock_guard lock{mutex_}; + progress_ += 1; + } + save_start_time(); + print_progress(); + } + + size_t current() { + std::lock_guard lock{mutex_}; + return std::min(static_cast(progress_), size_t(100)); + } + + bool is_completed() const { return get_value(); } + + void mark_as_completed() { + get_value() = true; + print_progress(); + } + +private: + + template + auto get_value() -> decltype((details::get_value(std::declval()).value)) { + return details::get_value(settings_).value; + } + + template + auto get_value() const -> decltype((details::get_value(std::declval()).value)) { + return details::get_value(settings_).value; + } + + Settings settings_; + float progress_{0.0}; + std::chrono::time_point start_time_point_; + std::mutex mutex_; + + template friend class MultiProgress; + std::atomic multi_progress_mode_{false}; + + void save_start_time() { + auto& show_elapsed_time = get_value(); + auto& saved_start_time = get_value(); + auto& show_remaining_time = get_value(); + if ((show_elapsed_time || show_remaining_time) && !saved_start_time) { + start_time_point_ = std::chrono::high_resolution_clock::now(); + saved_start_time = true; + } + } + + void print_progress(bool from_multi_progress = false) { + if (multi_progress_mode_ && !from_multi_progress) { + if (progress_ > 100.0) { + get_value() = true; + } + return; + } + std::lock_guard lock{mutex_}; + auto now = std::chrono::high_resolution_clock::now(); + auto elapsed = std::chrono::duration_cast(now - start_time_point_); + + std::cout << termcolor::bold; + details::set_stream_color(std::cout, get_value()); + std::cout << get_value(); + std::cout << get_value(); + + details::BlockProgressScaleWriter writer{std::cout, get_value()}; + writer.write(progress_); + + std::cout << get_value(); + if (get_value()) { + std::cout << " " << std::min(static_cast(progress_), size_t(100)) << "%"; + } + + if (get_value()) { + std::cout << " ["; + details::write_duration(std::cout, elapsed); + } + + if (get_value()) { + if (get_value()) + std::cout << "<"; + else + std::cout << " ["; + auto eta = std::chrono::nanoseconds( + progress_ > 0 ? static_cast(elapsed.count() * 100 / progress_) : 0); + auto remaining = eta > elapsed ? (eta - elapsed) : (elapsed - eta); + details::write_duration(std::cout, remaining); + std::cout << "]"; + } else { + if (get_value()) + std::cout << "]"; + } + + if (get_value() == 0) + get_value() = 10; + std::cout << " " << get_value() << std::string(get_value(), ' ') << "\r"; + std::cout.flush(); + if (progress_ > 100.0) { + get_value() = true; + } + if (get_value() && !from_multi_progress) // Don't std::endl if calling from MultiProgress + std::cout << termcolor::reset << std::endl; + } +}; + +} // namespace indicators + +namespace indicators { + +class ProgressSpinner { + using Settings = std::tuple< + option::ForegroundColor, + option::PrefixText, + option::PostfixText, + option::ShowPercentage, + option::ShowElapsedTime, + option::ShowRemainingTime, + option::ShowSpinner, + option::SavedStartTime, + option::Completed, + option::MaxPostfixTextLen, + option::SpinnerStates + >; +public: + template ::type...>::value, void*>::type = nullptr> + explicit ProgressSpinner(Args&&... args) : settings_( + details::get(option::ForegroundColor{Color::white}, std::forward(args)...), + details::get(option::PrefixText{}, std::forward(args)...), + details::get(option::PostfixText{}, std::forward(args)...), + details::get(option::ShowPercentage{true}, std::forward(args)...), + details::get(option::ShowElapsedTime{false}, std::forward(args)...), + details::get(option::ShowRemainingTime{false}, std::forward(args)...), + details::get(option::ShowSpinner{true}, std::forward(args)...), + details::get(option::SavedStartTime{false}, std::forward(args)...), + details::get(option::Completed{false}, std::forward(args)...), + details::get(option::MaxPostfixTextLen{0}, std::forward(args)...), + details::get(option::SpinnerStates{std::vector{"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"}}, std::forward(args)...) + ){} + + template + void set_option(details::Setting&& setting){ + static_assert(!std::is_same(std::declval()))>::type>::value, "Setting has wrong type!"); + std::lock_guard lock(mutex_); + get_value() = std::move(setting).value; + } + + template + void set_option(const details::Setting& setting){ + static_assert(!std::is_same(std::declval()))>::type>::value, "Setting has wrong type!"); + std::lock_guard lock(mutex_); + get_value() = setting.value; + } + + void set_option(const details::Setting& setting){ + std::lock_guard lock(mutex_); + get_value() = setting.value; + if(setting.value.length() > get_value()){ + get_value() = setting.value.length(); + } + } + + void set_option(details::Setting&& setting){ + std::lock_guard lock(mutex_); + get_value() = std::move(setting).value; + auto& new_value = get_value(); + if(new_value.length() > get_value()){ + get_value() = new_value.length(); + } + } + + void set_progress(float value) { + { + std::lock_guard lock{mutex_}; + progress_ = value; + } + save_start_time(); + print_progress(); + } + + void tick() { + { + std::lock_guard lock{mutex_}; + progress_ += 1; + } + save_start_time(); + print_progress(); + } + + size_t current() { + std::lock_guard lock{mutex_}; + return std::min(static_cast(progress_), size_t(100)); + } + + bool is_completed() const { return get_value(); } + + void mark_as_completed() { + get_value() = true; + print_progress(); + } + +private: + Settings settings_; + float progress_{0.0}; + size_t index_{0}; + std::chrono::time_point start_time_point_; + std::mutex mutex_; + + template + auto get_value() -> decltype((details::get_value(std::declval()).value)) { + return details::get_value(settings_).value; + } + + template + auto get_value() const -> decltype((details::get_value(std::declval()).value)) { + return details::get_value(settings_).value; + } + + void save_start_time() { + auto& show_elapsed_time = get_value(); + auto& show_remaining_time = get_value(); + auto& saved_start_time = get_value(); + if ((show_elapsed_time || show_remaining_time) && !saved_start_time) { + start_time_point_ = std::chrono::high_resolution_clock::now(); + saved_start_time = true; + } + } + + void print_progress() { + std::lock_guard lock{mutex_}; + auto now = std::chrono::high_resolution_clock::now(); + auto elapsed = std::chrono::duration_cast(now - start_time_point_); + + std::cout << termcolor::bold; + details::set_stream_color(std::cout, get_value()); + std::cout << get_value(); + if (get_value()) + std::cout << get_value()[index_ % get_value().size()]; + if (get_value()) { + std::cout << " " << std::min(static_cast(progress_), size_t(100)) << "%"; + } + + if (get_value()) { + std::cout << " ["; + details::write_duration(std::cout, elapsed); + } + + if (get_value()) { + if (get_value()) + std::cout << "<"; + else + std::cout << " ["; + auto eta = std::chrono::nanoseconds( + progress_ > 0 ? static_cast(elapsed.count() * 100 / progress_) : 0); + auto remaining = eta > elapsed ? (eta - elapsed) : (elapsed - eta); + details::write_duration(std::cout, remaining); + std::cout << "]"; + } else { + if (get_value()) + std::cout << "]"; + } + + if (get_value() == 0) + get_value() = 10; + std::cout << " " << get_value() << std::string(get_value(), ' ') << "\r"; + std::cout.flush(); + index_ += 1; + if (progress_ > 100.0) { + get_value() = true; + } + if (get_value()) + std::cout << termcolor::reset << std::endl; + } +}; + +} // namespace indicators + +namespace indicators { + +template class MultiProgress { +public: + template ::type> + explicit MultiProgress(Indicators &... bars) { + bars_ = {bars...}; + for (auto &bar : bars_) { + bar.get().multi_progress_mode_ = true; + } + } + + template + typename std::enable_if<(index >= 0 && index < count), void>::type set_progress(float value) { + if (!bars_[index].get().is_completed()) + bars_[index].get().set_progress(value); + print_progress(); + } + + template + typename std::enable_if<(index >= 0 && index < count), void>::type tick() { + if (!bars_[index].get().is_completed()) + bars_[index].get().tick(); + print_progress(); + } + + template + typename std::enable_if<(index >= 0 && index < count), bool>::type is_completed() const { + return bars_[index].get().is_completed(); + } + +private: + std::atomic started_{false}; + std::mutex mutex_; + std::vector> bars_; + + bool _all_completed() { + bool result{true}; + for (size_t i = 0; i < count; ++i) + result &= bars_[i].get().is_completed(); + return result; + } + + void print_progress() { + std::lock_guard lock{mutex_}; + if (started_) + for (size_t i = 0; i < count; ++i) + std::cout << "\x1b[A"; + for (auto &bar : bars_) { + bar.get().print_progress(true); + std::cout << "\n"; + } + std::cout << termcolor::reset; + if (!started_) + started_ = true; + } +}; + +} // namespace indicators