1 module progress; 2 3 import std.stdio; 4 import std.range; 5 import std.format; 6 import std.datetime; 7 import core.sys.posix.unistd; 8 import core.sys.posix.sys.ioctl; 9 10 class Progress 11 { 12 private: 13 14 immutable static size_t default_width = 80; 15 size_t max_width = 40; 16 size_t width = default_width; 17 18 ulong start_time; 19 string caption = "Progress"; 20 size_t iterations; 21 size_t counter; 22 23 24 size_t getTerminalWidth() { 25 size_t column; 26 winsize ws; 27 if(ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) != -1) { 28 column = ws.ws_col; 29 } 30 if(column == 0) column = default_width; 31 32 return column; 33 } 34 35 36 void clear() { 37 write("\r"); 38 for(auto i = 0; i < width; i++) write(" "); 39 write("\r"); 40 } 41 42 43 int calc_eta() { 44 immutable auto ratio = cast(double)counter / iterations; 45 auto current_time = Clock.currTime.toUnixTime(); 46 auto duration = (current_time - start_time); 47 int hours, minutes, seconds; 48 double elapsed = (current_time - start_time); 49 int eta_sec = cast(int)((elapsed / ratio) - elapsed); 50 51 return eta_sec; 52 } 53 54 55 string progressbarText(string header_text, string footer_text) { 56 immutable auto ratio = cast(double)counter / iterations; 57 string result = ""; 58 59 double bar_length = width - header_text.length - footer_text.length; 60 if(bar_length > max_width && max_width > 0) { 61 bar_length = max_width; 62 } 63 size_t i = 0; 64 for(; i < ratio * bar_length; i++) result ~= "o"; 65 for(; i < bar_length; i++) result ~= " "; 66 67 return header_text ~ result ~ footer_text; 68 } 69 70 71 void print() { 72 immutable auto ratio = cast(double)counter / iterations; 73 auto header = appender!string(); 74 auto footer = appender!string(); 75 76 header.formattedWrite("%s %3d%% |", caption, cast(int)(ratio * 100)); 77 78 if(counter <= 1 || ratio == 0.0) { 79 footer.formattedWrite("| ETA --:--:--:"); 80 } else { 81 int h, m, s; 82 dur!"seconds"(calc_eta()) 83 .split!("hours", "minutes", "seconds")(h, m, s); 84 footer.formattedWrite("| ETA %02d:%02d:%02d ", h, m, s); 85 } 86 87 write(progressbarText(header.data, footer.data)); 88 } 89 90 91 void update() { 92 width = getTerminalWidth(); 93 94 clear(); 95 96 print(); 97 stdout.flush(); 98 } 99 100 101 public: 102 103 this(size_t iterations) { 104 if(iterations <= 0) iterations = 1; 105 106 counter = 0; 107 this.iterations = iterations; 108 start_time = Clock.currTime.toUnixTime; 109 } 110 111 @property { 112 string title() { return caption; } 113 string title(string text) { return caption = text; } 114 } 115 116 @property { 117 size_t count() { return counter; } 118 size_t count(size_t val) { 119 if(val > iterations) val = iterations; 120 return counter = val; 121 } 122 } 123 124 @property { 125 size_t maxWidth() { return max_width; } 126 size_t maxWidth(size_t w) { 127 return max_width = w; 128 } 129 } 130 131 void reset() { 132 counter = 0; 133 start_time = Clock.currTime.toUnixTime; 134 } 135 136 void next() { 137 counter++; 138 if(counter > iterations) counter = iterations; 139 140 update(); 141 } 142 143 144 } 145