#ifndef INDICATORS_PROGRESS_SPINNER #define INDICATORS_PROGRESS_SPINNER #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace indicators { class ProgressSpinner { using Settings = std::tuple; public: template ::type...>::value, void *>::type = nullptr> explicit ProgressSpinner(Args &&... args) : settings_( details::get( option::ForegroundColor{Color::unspecified}, 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)...), details::get( option::FontStyles{std::vector{}}, std::forward(args)...), details::get(option::MaxProgress{100}, std::forward(args)...), details::get(option::Stream{std::cout}, 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(size_t 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)(progress_, size_t(get_value())); } bool is_completed() const { return get_value(); } void mark_as_completed() { get_value() = true; print_progress(); } private: Settings settings_; size_t progress_{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; } } public: void print_progress() { std::lock_guard lock{mutex_}; auto &os = get_value(); const auto max_progress = get_value(); auto now = std::chrono::high_resolution_clock::now(); auto elapsed = std::chrono::duration_cast(now - start_time_point_); if (get_value() != Color::unspecified) details::set_stream_color(os, get_value()); for (auto &style : get_value()) details::set_font_style(os, style); os << get_value(); if (get_value()) os << get_value() [index_ % get_value().size()]; if (get_value()) { os << " " << std::size_t(progress_ / double(max_progress) * 100) << "%"; } if (get_value()) { os << " ["; details::write_duration(os, elapsed); } if (get_value()) { if (get_value()) os << "<"; else os << " ["; auto eta = std::chrono::nanoseconds( progress_ > 0 ? static_cast(std::ceil(float(elapsed.count()) * max_progress / progress_)) : 0); auto remaining = eta > elapsed ? (eta - elapsed) : (elapsed - eta); details::write_duration(os, remaining); os << "]"; } else { if (get_value()) os << "]"; } if (get_value() == 0) get_value() = 10; os << " " << get_value() << std::string(get_value(), ' ') << "\r"; os.flush(); index_ += 1; if (progress_ > max_progress) { get_value() = true; } if (get_value()) os << termcolor::reset << std::endl; } }; } // namespace indicators #endif