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