1 module dstatus.status;
2 
3 import std.conv : text, to;
4 import std.format : format;
5 import std.range : repeat, take;
6 import std.stdio : File, stdout, stderr;
7 import std.string : leftJustify, rightJustify;
8 
9 import dstatus.terminal;
10 
11 class Status {
12     private {
13         size_t _writeLength = 0;
14         size_t _prevReportLength = 0;
15         TerminalPosition _originalPosition;
16         TerminalPosition _currentPosition;
17     }
18 
19     File output;
20 
21     this() {
22         output = stderr;
23         anchor();
24     }
25 
26     private TerminalPosition getPosition() {
27         return new TerminalPosition(output);
28     }
29 
30     private void _write(string txt) {
31         /* Reset the cursor to the current reporting position */
32         _currentPosition.restore();
33 
34         // Write the new text
35         output.write(txt);
36 
37         /* If text is shorter than the previous length,
38            overwrite the remainder with spaces. */
39         if (txt.length < _prevReportLength) {
40             auto endPosition = getPosition();
41             scope(exit) endPosition.restore();
42 
43             auto remainderLength = _prevReportLength - txt.length;
44             output.write(' '.repeat(remainderLength));
45         }
46     }
47 
48     /* Anchor to the current cursor position. */
49     final void anchor() {
50         _currentPosition = _originalPosition = getPosition();
51         _writeLength = 0;
52         _prevReportLength = 0;
53     }
54 
55     final void clear() {
56         /* Reset the cursor to its original starting position. */
57         _originalPosition.restore();
58 
59         /* Clear all text written by us */
60         auto clearLength = _writeLength + _prevReportLength;
61         output.write(' '.repeat(clearLength));
62 
63         /* Reset the cursor to its original starting position. */
64         _originalPosition.restore();
65 
66         _writeLength = 0;
67         _prevReportLength = 0;
68     }
69 
70     final void write(T...)(T args) {
71         auto txt = text(args);
72 
73         _write(txt);
74         _currentPosition = getPosition();
75 
76         _writeLength += txt.length;
77         _prevReportLength = 0;
78     }
79 
80     final void report(T...)(T args) {
81         auto txt = text(args);
82 
83         _write(txt);
84 
85         // Store the length of the new text
86         _prevReportLength = txt.length;
87     }
88 }
89 
90 auto status() {
91     return new Status();
92 }
93 
94 
95 @safe:
96 
97 pure string makeFixedWidth(string truncatedSuffix = "...", alias justify = leftJustify, T...)(in size_t width, in T args) {
98     auto str = text(args);
99 
100     if (str.length > width) {
101         return "%s%s".format(str.take(width - truncatedSuffix.length), truncatedSuffix);
102     }
103 
104     return justify(str, width);
105 }
106 
107 unittest {
108     enum testString = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
109 
110     assert(makeFixedWidth(testString.length, testString) == testString);
111     assert(makeFixedWidth(10, testString) == "ABCDEFG...");
112     assert(makeFixedWidth(27, testString) == "ABCDEFGHIJKLMNOPQRSTUVWXYZ ");
113     assert(makeFixedWidth!("")(10, testString) == "ABCDEFGHIJ");
114 }