1 
2 #ifndef INDICATORS_STREAM_HELPER
3 #define INDICATORS_STREAM_HELPER
4 
5 #include <indicators/display_width.hpp>
6 #include <indicators/setting.hpp>
7 #include <indicators/termcolor.hpp>
8 
9 #include <algorithm>
10 #include <chrono>
11 #include <iomanip>
12 #include <ostream>
13 #include <string>
14 #include <vector>
15 
16 #include <cassert>
17 #include <cmath>
18 
19 namespace indicators {
20 namespace details {
21 
set_stream_color(std::ostream & os,Color color)22 inline void set_stream_color(std::ostream &os, Color color) {
23   switch (color) {
24   case Color::grey:
25     os << termcolor::grey;
26     break;
27   case Color::red:
28     os << termcolor::red;
29     break;
30   case Color::green:
31     os << termcolor::green;
32     break;
33   case Color::yellow:
34     os << termcolor::yellow;
35     break;
36   case Color::blue:
37     os << termcolor::blue;
38     break;
39   case Color::magenta:
40     os << termcolor::magenta;
41     break;
42   case Color::cyan:
43     os << termcolor::cyan;
44     break;
45   case Color::white:
46     os << termcolor::white;
47     break;
48   default:
49     assert(false);
50   }
51 }
52 
set_font_style(std::ostream & os,FontStyle style)53 inline void set_font_style(std::ostream &os, FontStyle style) {
54   switch (style) {
55   case FontStyle::bold:
56     os << termcolor::bold;
57     break;
58   case FontStyle::dark:
59     os << termcolor::dark;
60     break;
61   case FontStyle::italic:
62     os << termcolor::italic;
63     break;
64   case FontStyle::underline:
65     os << termcolor::underline;
66     break;
67   case FontStyle::blink:
68     os << termcolor::blink;
69     break;
70   case FontStyle::reverse:
71     os << termcolor::reverse;
72     break;
73   case FontStyle::concealed:
74     os << termcolor::concealed;
75     break;
76   case FontStyle::crossed:
77     os << termcolor::crossed;
78     break;
79   default:
80     break;
81   }
82 }
83 
write_duration(std::ostream & os,std::chrono::nanoseconds ns)84 inline std::ostream &write_duration(std::ostream &os, std::chrono::nanoseconds ns) {
85   using namespace std;
86   using namespace std::chrono;
87   using days = duration<int, ratio<86400>>;
88   char fill = os.fill();
89   os.fill('0');
90   auto d = duration_cast<days>(ns);
91   ns -= d;
92   auto h = duration_cast<hours>(ns);
93   ns -= h;
94   auto m = duration_cast<minutes>(ns);
95   ns -= m;
96   auto s = duration_cast<seconds>(ns);
97   if (d.count() > 0)
98     os << setw(2) << d.count() << "d:";
99   if (h.count() > 0)
100     os << setw(2) << h.count() << "h:";
101   os << setw(2) << m.count() << "m:" << setw(2) << s.count() << 's';
102   os.fill(fill);
103   return os;
104 }
105 
106 class BlockProgressScaleWriter {
107 public:
BlockProgressScaleWriter(std::ostream & os,size_t bar_width)108   BlockProgressScaleWriter(std::ostream &os, size_t bar_width) : os(os), bar_width(bar_width) {}
109 
write(float progress)110   std::ostream &write(float progress) {
111     std::string fill_text{"█"};
112     std::vector<std::string> lead_characters{" ", "▏", "▎", "▍", "▌", "▋", "▊", "▉"};
113     auto value = std::min(1.0f, std::max(0.0f, progress / 100.0f));
114     auto whole_width = std::floor(value * bar_width);
115     auto remainder_width = fmod((value * bar_width), 1.0f);
116     auto part_width = std::floor(remainder_width * lead_characters.size());
117     std::string lead_text = lead_characters[size_t(part_width)];
118     if ((bar_width - whole_width - 1) < 0)
119       lead_text = "";
120     for (size_t i = 0; i < whole_width; ++i)
121       os << fill_text;
122     os << lead_text;
123     for (size_t i = 0; i < (bar_width - whole_width - 1); ++i)
124       os << " ";
125     return os;
126   }
127 
128 private:
129   std::ostream &os;
130   size_t bar_width = 0;
131 };
132 
133 class ProgressScaleWriter {
134 public:
ProgressScaleWriter(std::ostream & os,size_t bar_width,const std::string & fill,const std::string & lead,const std::string & remainder)135   ProgressScaleWriter(std::ostream &os, size_t bar_width, const std::string &fill,
136                       const std::string &lead, const std::string &remainder)
137       : os(os), bar_width(bar_width), fill(fill), lead(lead), remainder(remainder) {}
138 
write(float progress)139   std::ostream &write(float progress) {
140     auto pos = static_cast<size_t>(progress * bar_width / 100.0);
141     for (size_t i = 0, current_display_width = 0; i < bar_width;) {
142       std::string next;
143 
144       if (i < pos) {
145         next = fill;
146         current_display_width = unicode::display_width(fill);
147       } else if (i == pos) {
148         next = lead;
149         current_display_width = unicode::display_width(lead);
150       } else {
151         next = remainder;
152         current_display_width = unicode::display_width(remainder);
153       }
154 
155       i += current_display_width;
156 
157       if (i > bar_width) {
158         // `next` is larger than the allowed bar width
159         // fill with empty space instead
160         os << std::string((bar_width - (i - current_display_width)), ' ');
161         break;
162       }
163 
164       os << next;
165     }
166     return os;
167   }
168 
169 private:
170   std::ostream &os;
171   size_t bar_width = 0;
172   std::string fill;
173   std::string lead;
174   std::string remainder;
175 };
176 
177 class IndeterminateProgressScaleWriter {
178 public:
IndeterminateProgressScaleWriter(std::ostream & os,size_t bar_width,const std::string & fill,const std::string & lead)179   IndeterminateProgressScaleWriter(std::ostream &os, size_t bar_width, const std::string &fill,
180                                    const std::string &lead)
181       : os(os), bar_width(bar_width), fill(fill), lead(lead) {}
182 
write(size_t progress)183   std::ostream &write(size_t progress) {
184     for (size_t i = 0; i < bar_width;) {
185       std::string next;
186       size_t current_display_width = 0;
187 
188       if (i < progress) {
189         next = fill;
190         current_display_width = unicode::display_width(fill);
191       } else if (i == progress) {
192         next = lead;
193         current_display_width = unicode::display_width(lead);
194       } else {
195         next = fill;
196         current_display_width = unicode::display_width(fill);
197       }
198 
199       i += current_display_width;
200 
201       if (i > bar_width) {
202         // `next` is larger than the allowed bar width
203         // fill with empty space instead
204         os << std::string((bar_width - (i - current_display_width)), ' ');
205         break;
206       }
207 
208       os << next;
209     }
210     return os;
211   }
212 
213 private:
214   std::ostream &os;
215   size_t bar_width = 0;
216   std::string fill;
217   std::string lead;
218 };
219 
220 } // namespace details
221 } // namespace indicators
222 
223 #endif