cli: Add pImpl to libdnf5::cli::progressbar::ProgressBar
This commit is contained in:
parent
8afd594761
commit
dc068cf54c
|
@ -23,6 +23,8 @@ along with libdnf. If not, see <https://www.gnu.org/licenses/>.
|
|||
|
||||
#include "libdnf5-cli/defs.h"
|
||||
|
||||
#include "libdnf5/common/impl_ptr.hpp"
|
||||
|
||||
#include <chrono>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
@ -54,64 +56,61 @@ public:
|
|||
|
||||
explicit ProgressBar(int64_t total_ticks);
|
||||
explicit ProgressBar(int64_t total_ticks, const std::string & description);
|
||||
virtual ~ProgressBar() = default;
|
||||
virtual ~ProgressBar();
|
||||
ProgressBar(const ProgressBar & src);
|
||||
ProgressBar & operator=(const ProgressBar & src);
|
||||
ProgressBar(ProgressBar && src) noexcept;
|
||||
ProgressBar & operator=(ProgressBar && src) noexcept;
|
||||
|
||||
void reset();
|
||||
|
||||
// ticks
|
||||
int64_t get_ticks() const noexcept { return ticks; }
|
||||
int64_t get_ticks() const noexcept;
|
||||
void set_ticks(int64_t value);
|
||||
void add_ticks(int64_t value);
|
||||
|
||||
// total ticks
|
||||
int64_t get_total_ticks() const noexcept { return total_ticks; }
|
||||
int64_t get_total_ticks() const noexcept;
|
||||
void set_total_ticks(int64_t value);
|
||||
|
||||
// number
|
||||
int32_t get_number() const noexcept { return number; }
|
||||
void set_number(int32_t value) { number = value; }
|
||||
int32_t get_number() const noexcept;
|
||||
void set_number(int32_t value);
|
||||
|
||||
// total
|
||||
int32_t get_total() const noexcept { return total; }
|
||||
void set_total(int32_t value) { total = value; }
|
||||
int32_t get_total() const noexcept;
|
||||
void set_total(int32_t value);
|
||||
|
||||
// workflow
|
||||
void start();
|
||||
ProgressBarState get_state() const noexcept { return state; }
|
||||
void set_state(ProgressBarState value) {
|
||||
update();
|
||||
state = value;
|
||||
}
|
||||
bool is_finished() const noexcept {
|
||||
return get_state() != ProgressBarState::READY && get_state() != ProgressBarState::STARTED;
|
||||
}
|
||||
bool is_failed() const noexcept {
|
||||
return get_state() == ProgressBarState::ERROR || get_state() == ProgressBarState::WARNING;
|
||||
}
|
||||
ProgressBarState get_state() const noexcept;
|
||||
void set_state(ProgressBarState value);
|
||||
bool is_finished() const noexcept;
|
||||
bool is_failed() const noexcept;
|
||||
|
||||
// description
|
||||
std::string get_description() const noexcept { return description; }
|
||||
void set_description(const std::string & value) { description = value; }
|
||||
std::string get_description() const noexcept;
|
||||
void set_description(const std::string & value);
|
||||
|
||||
// messages
|
||||
void add_message(MessageType type, const std::string & message) { messages.emplace_back(type, message); }
|
||||
void add_message(MessageType type, const std::string & message);
|
||||
/// remove the last message
|
||||
void pop_message();
|
||||
const std::vector<Message> & get_messages() const noexcept { return messages; }
|
||||
const std::vector<Message> & get_messages() const noexcept;
|
||||
|
||||
// auto-finish feature; turn off if you want to handle state manually
|
||||
bool get_auto_finish() const noexcept { return auto_finish; }
|
||||
void set_auto_finish(bool value) { auto_finish = value; }
|
||||
bool get_auto_finish() const noexcept;
|
||||
void set_auto_finish(bool value);
|
||||
|
||||
// stats
|
||||
void update();
|
||||
int32_t get_percent_done() const noexcept { return percent_done; }
|
||||
int64_t get_current_speed() const noexcept { return current_speed; }
|
||||
int64_t get_average_speed() const noexcept { return average_speed; }
|
||||
int64_t get_elapsed_seconds() const noexcept { return elapsed_seconds; }
|
||||
int64_t get_remaining_seconds() const noexcept { return remaining_seconds; }
|
||||
int32_t get_percent_done() const noexcept;
|
||||
int64_t get_current_speed() const noexcept;
|
||||
int64_t get_average_speed() const noexcept;
|
||||
int64_t get_elapsed_seconds() const noexcept;
|
||||
int64_t get_remaining_seconds() const noexcept;
|
||||
|
||||
std::chrono::time_point<std::chrono::system_clock> get_begin() { return begin; }
|
||||
std::chrono::time_point<std::chrono::system_clock> get_begin();
|
||||
|
||||
LIBDNF_CLI_API friend std::ostream & operator<<(std::ostream & os, ProgressBar & bar);
|
||||
|
||||
|
@ -119,36 +118,8 @@ protected:
|
|||
virtual void to_stream(std::ostream & stream) = 0;
|
||||
|
||||
private:
|
||||
// ticks
|
||||
int64_t ticks = -1;
|
||||
int64_t total_ticks = -1;
|
||||
|
||||
// numbers
|
||||
int32_t number = 0;
|
||||
int32_t total = 0;
|
||||
|
||||
// description
|
||||
std::string description;
|
||||
|
||||
// messages
|
||||
std::vector<Message> messages;
|
||||
|
||||
ProgressBarState state = ProgressBarState::READY;
|
||||
|
||||
int32_t percent_done = -1;
|
||||
int64_t elapsed_seconds = 0;
|
||||
int64_t remaining_seconds = 0;
|
||||
int64_t average_speed = 0;
|
||||
|
||||
bool auto_finish = true;
|
||||
|
||||
std::chrono::time_point<std::chrono::system_clock> begin = std::chrono::system_clock::from_time_t(0);
|
||||
std::chrono::time_point<std::chrono::system_clock> end = std::chrono::system_clock::from_time_t(0);
|
||||
|
||||
// current (average) speed over the last second
|
||||
std::chrono::time_point<std::chrono::system_clock> current_speed_window_start = std::chrono::system_clock::now();
|
||||
int64_t current_speed = 0;
|
||||
int64_t current_speed_window_ticks = 0;
|
||||
class LIBDNF_CLI_LOCAL Impl;
|
||||
ImplPtr<Impl> p_impl;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -23,33 +23,164 @@ along with libdnf. If not, see <https://www.gnu.org/licenses/>.
|
|||
|
||||
namespace libdnf5::cli::progressbar {
|
||||
|
||||
class ProgressBar::Impl {
|
||||
public:
|
||||
explicit Impl(int64_t total_ticks) : total_ticks{total_ticks} {}
|
||||
Impl(int64_t total_ticks, const std::string & description) : total_ticks{total_ticks}, description{description} {}
|
||||
|
||||
ProgressBar::ProgressBar(int64_t total_ticks) : total_ticks{total_ticks} {}
|
||||
// ticks
|
||||
int64_t ticks = -1;
|
||||
int64_t total_ticks = -1;
|
||||
|
||||
// numbers
|
||||
int32_t number = 0;
|
||||
int32_t total = 0;
|
||||
|
||||
// description
|
||||
std::string description;
|
||||
|
||||
// messages
|
||||
std::vector<ProgressBar::Message> messages;
|
||||
|
||||
ProgressBarState state = ProgressBarState::READY;
|
||||
|
||||
int32_t percent_done = -1;
|
||||
int64_t elapsed_seconds = 0;
|
||||
int64_t remaining_seconds = 0;
|
||||
int64_t average_speed = 0;
|
||||
|
||||
bool auto_finish = true;
|
||||
|
||||
std::chrono::time_point<std::chrono::system_clock> begin = std::chrono::system_clock::from_time_t(0);
|
||||
std::chrono::time_point<std::chrono::system_clock> end = std::chrono::system_clock::from_time_t(0);
|
||||
|
||||
// current (average) speed over the last second
|
||||
std::chrono::time_point<std::chrono::system_clock> current_speed_window_start = std::chrono::system_clock::now();
|
||||
int64_t current_speed = 0;
|
||||
int64_t current_speed_window_ticks = 0;
|
||||
};
|
||||
|
||||
|
||||
ProgressBar::~ProgressBar() = default;
|
||||
|
||||
ProgressBar::ProgressBar(int64_t total_ticks) : p_impl(new Impl(total_ticks)) {}
|
||||
|
||||
ProgressBar::ProgressBar(int64_t total_ticks, const std::string & description)
|
||||
: total_ticks{total_ticks},
|
||||
description{description} {}
|
||||
: p_impl(new Impl(total_ticks, description)) {}
|
||||
|
||||
ProgressBar::ProgressBar(const ProgressBar & src) = default;
|
||||
ProgressBar & ProgressBar::operator=(const ProgressBar & src) = default;
|
||||
ProgressBar::ProgressBar(ProgressBar && src) noexcept = default;
|
||||
ProgressBar & ProgressBar::operator=(ProgressBar && src) noexcept = default;
|
||||
|
||||
|
||||
int64_t ProgressBar::get_ticks() const noexcept {
|
||||
return p_impl->ticks;
|
||||
}
|
||||
|
||||
int64_t ProgressBar::get_total_ticks() const noexcept {
|
||||
return p_impl->total_ticks;
|
||||
}
|
||||
|
||||
int32_t ProgressBar::get_number() const noexcept {
|
||||
return p_impl->number;
|
||||
}
|
||||
|
||||
void ProgressBar::set_number(int32_t value) {
|
||||
p_impl->number = value;
|
||||
}
|
||||
|
||||
int32_t ProgressBar::get_total() const noexcept {
|
||||
return p_impl->total;
|
||||
}
|
||||
|
||||
void ProgressBar::set_total(int32_t value) {
|
||||
p_impl->total = value;
|
||||
}
|
||||
|
||||
ProgressBarState ProgressBar::get_state() const noexcept {
|
||||
return p_impl->state;
|
||||
}
|
||||
|
||||
void ProgressBar::set_state(ProgressBarState value) {
|
||||
update();
|
||||
p_impl->state = value;
|
||||
}
|
||||
|
||||
bool ProgressBar::is_finished() const noexcept {
|
||||
return get_state() != ProgressBarState::READY && get_state() != ProgressBarState::STARTED;
|
||||
}
|
||||
|
||||
bool ProgressBar::is_failed() const noexcept {
|
||||
return get_state() == ProgressBarState::ERROR || get_state() == ProgressBarState::WARNING;
|
||||
}
|
||||
|
||||
std::string ProgressBar::get_description() const noexcept {
|
||||
return p_impl->description;
|
||||
}
|
||||
|
||||
void ProgressBar::set_description(const std::string & value) {
|
||||
p_impl->description = value;
|
||||
}
|
||||
|
||||
void ProgressBar::add_message(MessageType type, const std::string & message) {
|
||||
p_impl->messages.emplace_back(type, message);
|
||||
}
|
||||
|
||||
const std::vector<ProgressBar::Message> & ProgressBar::get_messages() const noexcept {
|
||||
return p_impl->messages;
|
||||
}
|
||||
|
||||
bool ProgressBar::get_auto_finish() const noexcept {
|
||||
return p_impl->auto_finish;
|
||||
}
|
||||
|
||||
void ProgressBar::set_auto_finish(bool value) {
|
||||
p_impl->auto_finish = value;
|
||||
}
|
||||
|
||||
int32_t ProgressBar::get_percent_done() const noexcept {
|
||||
return p_impl->percent_done;
|
||||
}
|
||||
|
||||
int64_t ProgressBar::get_current_speed() const noexcept {
|
||||
return p_impl->current_speed;
|
||||
}
|
||||
|
||||
int64_t ProgressBar::get_average_speed() const noexcept {
|
||||
return p_impl->average_speed;
|
||||
}
|
||||
|
||||
int64_t ProgressBar::get_elapsed_seconds() const noexcept {
|
||||
return p_impl->elapsed_seconds;
|
||||
}
|
||||
|
||||
int64_t ProgressBar::get_remaining_seconds() const noexcept {
|
||||
return p_impl->remaining_seconds;
|
||||
}
|
||||
|
||||
std::chrono::time_point<std::chrono::system_clock> ProgressBar::get_begin() {
|
||||
return p_impl->begin;
|
||||
}
|
||||
|
||||
void ProgressBar::reset() {
|
||||
ticks = -1;
|
||||
total_ticks = -1;
|
||||
number = 0;
|
||||
total = 0;
|
||||
description = "";
|
||||
messages.clear();
|
||||
state = ProgressBarState::READY;
|
||||
percent_done = -1;
|
||||
elapsed_seconds = 0;
|
||||
remaining_seconds = 0;
|
||||
average_speed = 0;
|
||||
auto_finish = true;
|
||||
begin = std::chrono::system_clock::from_time_t(0);
|
||||
end = std::chrono::system_clock::from_time_t(0);
|
||||
current_speed_window_start = std::chrono::system_clock::now();
|
||||
current_speed = 0;
|
||||
current_speed_window_ticks = 0;
|
||||
p_impl->ticks = -1;
|
||||
p_impl->total_ticks = -1;
|
||||
p_impl->number = 0;
|
||||
p_impl->total = 0;
|
||||
p_impl->description = "";
|
||||
p_impl->messages.clear();
|
||||
p_impl->state = ProgressBarState::READY;
|
||||
p_impl->percent_done = -1;
|
||||
p_impl->elapsed_seconds = 0;
|
||||
p_impl->remaining_seconds = 0;
|
||||
p_impl->average_speed = 0;
|
||||
p_impl->auto_finish = true;
|
||||
p_impl->begin = std::chrono::system_clock::from_time_t(0);
|
||||
p_impl->end = std::chrono::system_clock::from_time_t(0);
|
||||
p_impl->current_speed_window_start = std::chrono::system_clock::now();
|
||||
p_impl->current_speed = 0;
|
||||
p_impl->current_speed_window_ticks = 0;
|
||||
}
|
||||
|
||||
|
||||
|
@ -58,27 +189,27 @@ void ProgressBar::set_ticks(int64_t value) {
|
|||
return;
|
||||
}
|
||||
auto new_ticks = value;
|
||||
if (total_ticks >= 0) {
|
||||
new_ticks = std::min(new_ticks, total_ticks);
|
||||
if (p_impl->total_ticks >= 0) {
|
||||
new_ticks = std::min(new_ticks, p_impl->total_ticks);
|
||||
}
|
||||
if (new_ticks >= ticks) {
|
||||
current_speed_window_ticks += new_ticks - ticks;
|
||||
if (new_ticks >= p_impl->ticks) {
|
||||
p_impl->current_speed_window_ticks += new_ticks - p_impl->ticks;
|
||||
} else {
|
||||
current_speed_window_ticks = 0;
|
||||
p_impl->current_speed_window_ticks = 0;
|
||||
}
|
||||
ticks = new_ticks;
|
||||
p_impl->ticks = new_ticks;
|
||||
}
|
||||
|
||||
|
||||
void ProgressBar::add_ticks(int64_t value) {
|
||||
set_ticks(ticks + value);
|
||||
set_ticks(p_impl->ticks + value);
|
||||
}
|
||||
|
||||
|
||||
void ProgressBar::start() {
|
||||
if (begin == std::chrono::system_clock::from_time_t(0)) {
|
||||
begin = std::chrono::system_clock::now();
|
||||
state = ProgressBarState::STARTED;
|
||||
if (p_impl->begin == std::chrono::system_clock::from_time_t(0)) {
|
||||
p_impl->begin = std::chrono::system_clock::now();
|
||||
p_impl->state = ProgressBarState::STARTED;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -88,65 +219,66 @@ void ProgressBar::update() {
|
|||
return;
|
||||
}
|
||||
|
||||
if (total_ticks < 0) {
|
||||
if (p_impl->total_ticks < 0) {
|
||||
// unknown total ticks
|
||||
percent_done = -1;
|
||||
} else if (total_ticks == 0) {
|
||||
p_impl->percent_done = -1;
|
||||
} else if (p_impl->total_ticks == 0) {
|
||||
// can't divide by zero, consider progressbar 100% complete
|
||||
percent_done = 100;
|
||||
} else if (ticks >= total_ticks) {
|
||||
percent_done = 100;
|
||||
p_impl->percent_done = 100;
|
||||
} else if (p_impl->ticks >= p_impl->total_ticks) {
|
||||
p_impl->percent_done = 100;
|
||||
} else {
|
||||
percent_done = static_cast<int32_t>(static_cast<float>(ticks) / static_cast<float>(total_ticks) * 100);
|
||||
p_impl->percent_done =
|
||||
static_cast<int32_t>(static_cast<float>(p_impl->ticks) / static_cast<float>(p_impl->total_ticks) * 100);
|
||||
}
|
||||
|
||||
auto now = std::chrono::high_resolution_clock::now();
|
||||
|
||||
// compute the current speed (ticks per second)
|
||||
auto delta = now - current_speed_window_start;
|
||||
auto delta = now - p_impl->current_speed_window_start;
|
||||
auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(delta).count();
|
||||
|
||||
if (ms > 950) {
|
||||
current_speed = current_speed_window_ticks * 1000 / ms;
|
||||
p_impl->current_speed = p_impl->current_speed_window_ticks * 1000 / ms;
|
||||
// reset the window
|
||||
current_speed_window_ticks = 0;
|
||||
current_speed_window_start = now;
|
||||
} else if (current_speed == 0 && ms != 0) {
|
||||
current_speed = current_speed_window_ticks * 1000 / ms;
|
||||
p_impl->current_speed_window_ticks = 0;
|
||||
p_impl->current_speed_window_start = now;
|
||||
} else if (p_impl->current_speed == 0 && ms != 0) {
|
||||
p_impl->current_speed = p_impl->current_speed_window_ticks * 1000 / ms;
|
||||
}
|
||||
|
||||
// compute average speed
|
||||
delta = now - begin;
|
||||
delta = now - p_impl->begin;
|
||||
ms = std::chrono::duration_cast<std::chrono::milliseconds>(delta).count();
|
||||
if (ms == 0) {
|
||||
average_speed = 0;
|
||||
p_impl->average_speed = 0;
|
||||
} else {
|
||||
average_speed = ticks * 1000 / ms;
|
||||
p_impl->average_speed = p_impl->ticks * 1000 / ms;
|
||||
}
|
||||
|
||||
// compute elapsed seconds
|
||||
// round the result to display 00m00s less frequently
|
||||
elapsed_seconds = std::chrono::round<std::chrono::seconds>(delta).count();
|
||||
p_impl->elapsed_seconds = std::chrono::round<std::chrono::seconds>(delta).count();
|
||||
|
||||
// compute remaining time
|
||||
if (total_ticks >= 0) {
|
||||
int64_t remaining_ticks = total_ticks - ticks;
|
||||
if (current_speed != 0) {
|
||||
remaining_seconds = remaining_ticks / current_speed;
|
||||
if (p_impl->total_ticks >= 0) {
|
||||
int64_t remaining_ticks = p_impl->total_ticks - p_impl->ticks;
|
||||
if (p_impl->current_speed != 0) {
|
||||
p_impl->remaining_seconds = remaining_ticks / p_impl->current_speed;
|
||||
}
|
||||
} else {
|
||||
// unknown total ticks
|
||||
remaining_seconds = -1;
|
||||
p_impl->remaining_seconds = -1;
|
||||
}
|
||||
|
||||
if (total_ticks >= 0 && ticks >= total_ticks) {
|
||||
if (auto_finish && state == ProgressBarState::STARTED) {
|
||||
if (p_impl->total_ticks >= 0 && p_impl->ticks >= p_impl->total_ticks) {
|
||||
if (p_impl->auto_finish && p_impl->state == ProgressBarState::STARTED) {
|
||||
// avoid calling set_state() because it triggers update() and ends up in an endless recursion
|
||||
state = ProgressBarState::SUCCESS;
|
||||
p_impl->state = ProgressBarState::SUCCESS;
|
||||
}
|
||||
end = now;
|
||||
percent_done = 100;
|
||||
remaining_seconds = 0;
|
||||
p_impl->end = now;
|
||||
p_impl->percent_done = 100;
|
||||
p_impl->remaining_seconds = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -155,13 +287,13 @@ void ProgressBar::set_total_ticks(int64_t value) {
|
|||
if (is_finished()) {
|
||||
return;
|
||||
}
|
||||
total_ticks = value;
|
||||
p_impl->total_ticks = value;
|
||||
}
|
||||
|
||||
|
||||
void ProgressBar::pop_message() {
|
||||
if (!messages.empty()) {
|
||||
messages.pop_back();
|
||||
if (!p_impl->messages.empty()) {
|
||||
p_impl->messages.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue