1 module snck; 2 3 import std.range.primitives : isInputRange; 4 5 6 struct Snck(R) if (isInputRange!R) { 7 import std.range.primitives : ElementType, hasLength; 8 import std.stdio : File, stderr; 9 import std.array; // : empty, front, popFront; 10 import std.conv : to; 11 import std.datetime.stopwatch; 12 13 R range; 14 File file; 15 StopWatch watch = StopWatch(AutoStart.no); 16 alias E = ElementType!R; 17 size_t count = 0; 18 size_t nblocks = 10; 19 double minsecs = 0.1; 20 Duration previous; 21 22 this(R range) { 23 this.range = range; 24 this.file = stderr; 25 this.watch.start(); 26 } 27 28 @property empty() const { 29 return range.empty; 30 } 31 32 auto front() { 33 return range.front(); 34 } 35 36 void popFront() { 37 range.popFront; 38 ++count; 39 40 // prevent too frequent message 41 auto now = watch.peek; 42 auto secs = (now - previous).total!"nsecs" * 1e-9; 43 if (!range.empty && secs < minsecs) return; 44 45 file.write("\r"); 46 47 // display percentage 48 static if (hasLength!R) { 49 auto total = count + range.length; 50 file.writef!"%3d%s: "(100 * count / total, "%"); 51 } 52 file.writef!"%d"(this.count); 53 static if (hasLength!R) { 54 file.writef!"/%d"(total); 55 } 56 57 // TODO display progress bar 58 static if (hasLength!R) { 59 file.write("|"); 60 auto passed = nblocks * count / total; 61 foreach (i; 0 .. nblocks) { 62 if (i <= passed) { 63 file.write("█"); 64 } else { 65 file.write(" "); 66 } 67 } 68 file.write("|"); 69 } 70 71 // display elapsed time 72 file.writef!" ["; 73 this.printTime(now); 74 // display estimated amount of remaining time 75 static if (hasLength!R) { 76 auto fps = 1e9 * count / now.total!"nsecs"; 77 auto remained = dur!"seconds"(to!long(range.length.to!double / fps)); 78 file.write("<"); 79 this.printTime(remained); 80 file.writef!", %.2fit/s"(fps); 81 } 82 file.writef!"]"; 83 // file.writef("\n"); 84 85 if (this.range.empty) { 86 file.writef("\n"); 87 } 88 file.flush(); 89 this.previous = now; 90 } 91 92 void printTime(Duration d) { 93 auto s = d.split!("hours", "minutes", "seconds"); 94 if (s.hours > 0) { 95 file.writef!"%02d"(s.hours); 96 } else { 97 file.writef!"%02d:%02d"(s.minutes, s.seconds); 98 } 99 } 100 } 101 102 auto snck(R)(R range) { 103 return Snck!R(range); 104 } 105 106 107 108 unittest 109 { 110 import core.thread; 111 import std.range; 112 foreach (i; [1, 2, 3].snck) { 113 Thread.sleep(dur!"msecs"(i * 300)); 114 } 115 116 foreach (i; iota(100).snck) { 117 Thread.sleep(dur!"msecs"(10)); 118 } 119 }