1 /**
2 Copyright: Copyright (c) 2018, Joakim Brännström. All rights reserved.
3 License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0)
4 Author: Joakim Brännström (joakim.brannstrom@gmx.com)
5 */
6 module distssh.table;
7 
8 import logger = std.experimental.logger;
9 import std.exception : collectException;
10 
11 @safe:
12 
13 struct Table(int columnsNr) {
14     alias Row = string[columnsNr];
15 
16     Row heading_;
17     Row[] rows;
18     ulong[columnsNr] columnWidth;
19 
20     this(const Row heading) {
21         this.heading = heading;
22         updateColumns(heading);
23     }
24 
25     void heading(const Row r) {
26         heading_ = r;
27         updateColumns(r);
28     }
29 
30     void put(const Row r) {
31         rows ~= r;
32         updateColumns(r);
33     }
34 
35     import std.format : FormatSpec;
36 
37     void toString(Writer, Char)(scope Writer w, FormatSpec!Char fmt) const {
38         import std.ascii : newline;
39         import std.range : enumerate, repeat;
40         import std.format : formattedWrite;
41         import std.range.primitives : put;
42 
43         immutable sep = "|";
44         immutable lhs_sep = "| ";
45         immutable mid_sep = " | ";
46         immutable rhs_sep = " |";
47 
48         void printRow(const ref Row r) {
49             foreach (const r_; r[].enumerate) {
50                 if (r_.index == 0)
51                     put(w, lhs_sep);
52                 else
53                     put(w, mid_sep);
54                 formattedWrite(w, "%-*s", columnWidth[r_.index], r_.value);
55             }
56             put(w, rhs_sep);
57             put(w, newline);
58         }
59 
60         printRow(heading_);
61 
62         immutable dash = "-";
63         foreach (len; columnWidth) {
64             put(w, sep);
65             put(w, repeat(dash, len + 2));
66         }
67         put(w, sep);
68         put(w, newline);
69 
70         foreach (const ref r; rows) {
71             printRow(r);
72         }
73     }
74 
75     private void updateColumns(const ref Row r) nothrow {
76         import std.algorithm : filter, count, map;
77         import std.range : enumerate;
78         import std.uni : byGrapheme;
79         import std.typecons : tuple;
80 
81         try {
82             foreach (a; r[].enumerate
83                     .map!(a => tuple(a.index, a.value.byGrapheme.count))
84                     .filter!(a => a[1] > columnWidth[a[0]])) {
85                 columnWidth[a[0]] = a[1];
86             }
87         } catch (Exception e) {
88             logger.warning(e.msg).collectException;
89         }
90     }
91 }