1 /++
2  + Asciitable to nicely format tables of strings.
3  +
4  + Authors: Christian Koestlin
5  + Copyright: Copyright © 2018, Christian Köstlin
6  + License: MIT
7  +/
8 
9 module asciitable;
10 
11 public import asciitable.packageversion;
12 
13 import std..string;
14 
15 struct Row
16 {
17     string[] columns;
18     this(string[] data)
19     {
20         this.columns = data;
21     }
22 }
23 
24 /++
25  + The workhorse of the module.
26  +/
27 struct AsciiTable
28 {
29     ulong[] minimumWidths;
30     Row[] rows;
31 
32     this(W...)(W minimumWidths)
33     {
34         this.minimumWidths = [minimumWidths];
35     }
36 
37     /// Add a row of strings
38     AsciiTable add(V...)(V values)
39     {
40         if (values.length != minimumWidths.length)
41         {
42             throw new Exception("All rows must have length %s".format(minimumWidths.length));
43         }
44         rows ~= Row([values]);
45         return this;
46     }
47 
48     /// Convert to tabular presentation
49     string toString(string linePrefix = "", string separator = "")
50     {
51         import std.algorithm;
52         import std..string;
53 
54         foreach (row; rows)
55         {
56             foreach (idx, column; row.columns)
57             {
58                 minimumWidths[idx] = max(minimumWidths[idx], column.length);
59             }
60         }
61         string res = "";
62         foreach (row; rows)
63         {
64             if (res.length > 0)
65             {
66                 res ~= "\n";
67             }
68             res ~= linePrefix ~ separator;
69             foreach (idx, column; row.columns)
70             {
71                 res ~= leftJustify(column, minimumWidths[idx], ' ') ~ separator;
72             }
73         }
74         return res;
75     }
76 }
77 
78 ///
79 @("example") unittest
80 {
81     import unit_threaded;
82 
83     AsciiTable(1, 2, 3).add("1", "2", "3").add("4", "5", "6").toString("prefix",
84             "|").shouldEqual("prefix|1|2 |3  |\n" ~ "prefix|4|5 |6  |");
85 }
86 
87 @("wrong usage of ascii table") unittest
88 {
89     import unit_threaded;
90 
91     AsciiTable(1, 1, 1).add("1", "2").shouldThrow!Exception;
92 }
93 
94 @("auto expand columns") unittest
95 {
96     import unit_threaded;
97 
98     AsciiTable(1).add("test").toString.shouldEqual("test");
99 }