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 }