1 /******************************************************************************* 2 3 Helper template to display tables to the console where the headings of the 4 columns are the names of the fields of a struct, and where the contents of 5 each row of the table are the values of the fields of an instance of the 6 struct type. 7 8 Usage example: 9 10 --- 11 12 import ocean.io.console.StructTable; 13 14 struct Test 15 { 16 int number; 17 float fraction; 18 char[] name; 19 } 20 21 scope table = new StructTable!(Test); 22 23 table.addRow(Test(23, 12.12, "gavin")); 24 table.addRow(Test(17, 99.9, "david")); 25 table.addRow(Test(11, 10.0, "luca")); 26 27 table.display(); 28 29 --- 30 31 The output of the usage example would be: 32 33 --- 34 35 ---------------------------- 36 number | fraction | name | 37 ---------------------------- 38 23 | 12.12 | gavin | 39 17 | 99.90 | david | 40 11 | 10.00 | luca | 41 ---------------------------- 42 43 --- 44 45 The StructTable template class internally generates one protected method for 46 each field of the struct it is based on (the template parameter). These 47 methods are named <field name>_string, and return a char[] which is to be 48 displayed in the approrpiate column of the table. By default these methods 49 simply format the struct fields using Layout. However, due to the way this 50 is implemented, it is possible to derive a class which overrides the 51 stringifying method of one or more struct fields, enabling special output 52 behaviour to be implemented. 53 54 Class overriding usage example (extends above example): 55 56 --- 57 58 import ocean.text.convert.Formatter; 59 60 class TestTable : StructTable!(Test) 61 { 62 override protected char[] fraction_string ( float* field ) 63 { 64 this.format_buffer.length = 0; 65 sformat(this.format_buffer, "{}%", *field * 100.0); 66 return this.format_buffer; 67 } 68 } 69 70 scope table2 = new TestTable; 71 72 table2.addRow(Test(23, 0.12, "gavin")); 73 table2.addRow(Test(17, 0.999, "david")); 74 table2.addRow(Test(11, 0.1, "luca")); 75 76 table2.display(); 77 78 --- 79 80 The output of the usage example would be: 81 82 --- 83 84 ----------------------------- 85 number | fraction | name | 86 ---------------------------- 87 23 | 12.00% | gavin | 88 17 | 0.999% | david | 89 11 | 0.10% | luca | 90 ----------------------------- 91 92 --- 93 94 Copyright: 95 Copyright (c) 2009-2016 dunnhumby Germany GmbH. 96 All rights reserved. 97 98 License: 99 Boost Software License Version 1.0. See LICENSE_BOOST.txt for details. 100 Alternatively, this file may be distributed under the terms of the Tango 101 3-Clause BSD License (see LICENSE_BSD.txt for details). 102 103 *******************************************************************************/ 104 105 module ocean.io.console.StructTable; 106 107 108 109 110 import ocean.meta.types.Qualifiers; 111 112 import ocean.meta.codegen.Identifier /* : fieldIdentifier */; 113 114 import ocean.io.console.Tables; 115 116 import ocean.text.convert.Formatter; 117 118 119 120 /******************************************************************************* 121 122 Struct table template class. 123 124 Note: as this class formats and copies the string for each field in each 125 row, it is generally not advisable to instantiate it as scope. 126 127 Params: 128 S = type whose fields specify the columns of the table 129 130 *******************************************************************************/ 131 132 public class StructTable ( S ) 133 { 134 /*************************************************************************** 135 136 Table used for output. 137 138 ***************************************************************************/ 139 140 private Table table; 141 142 143 /*************************************************************************** 144 145 Array of cells used while building up rows (one cell per field of S). 146 147 ***************************************************************************/ 148 149 private Table.Row.Cell[] cells; 150 151 152 /*************************************************************************** 153 154 Buffer used for string formatting. 155 156 ***************************************************************************/ 157 158 protected char[] format_buffer; 159 160 161 /*************************************************************************** 162 163 Template to mix in a protected method per field of S. 164 165 The generated methods are of the form: 166 167 protected char[] <field_name>_string ( <field_type>* field ) 168 169 ***************************************************************************/ 170 171 private template CellMethods ( size_t i = 0 ) 172 { 173 static if ( i == S.tupleof.length ) 174 { 175 static immutable istring CellMethods = ""; 176 } 177 else 178 { 179 static immutable istring CellMethods = "protected char[] " 180 ~ identifier!(S.tupleof[i]) 181 ~ "_string(" ~ typeof(S.tupleof[i]).stringof 182 ~ "* field){return this.defaultFieldString(field);}" 183 ~ CellMethods!(i + 1); 184 } 185 } 186 187 // pragma(msg, CellMethods!()); 188 mixin(CellMethods!()); 189 190 191 /*************************************************************************** 192 193 Constructor. 194 195 ***************************************************************************/ 196 197 public this ( ) 198 { 199 this.table = new Table(S.tupleof.length); 200 201 this.clear(); 202 } 203 204 205 /*************************************************************************** 206 207 Adds a row to the table. 208 209 ***************************************************************************/ 210 211 public void addRow ( ref S item ) 212 { 213 this.cells.length = 0; 214 // pragma(msg, ContentsRow!()); 215 mixin(ContentsRow!()); 216 this.table.nextRow.set(this.cells); 217 } 218 219 220 /*************************************************************************** 221 222 Displays the table to the console. 223 224 TODO: allow specification of output (like in Tables) 225 226 ***************************************************************************/ 227 228 public void display ( ) 229 { 230 this.table.nextRow.setDivider(); 231 this.table.display(); 232 } 233 234 235 /*************************************************************************** 236 237 Clears all rows from the table and adds the header row (containing the 238 names of all struct fields). 239 240 ***************************************************************************/ 241 242 public void clear ( ) 243 { 244 this.table.firstRow.setDivider(); 245 246 this.cells.length = 0; 247 mixin(HeaderRow!()); 248 249 this.table.nextRow.set(this.cells); 250 this.table.nextRow.setDivider(); 251 } 252 253 254 /*************************************************************************** 255 256 Default field formatting method template. Simply uses Layout to generate 257 a string for a field. 258 259 Params: 260 T = type of field in S 261 field = pointer to a field of type T in a struct of type S 262 263 Returns: 264 string representation of the passed field 265 266 ***************************************************************************/ 267 268 private char[] defaultFieldString ( T ) ( T* field ) 269 { 270 this.format_buffer.length = 0; 271 assumeSafeAppend(this.format_buffer); 272 sformat(this.format_buffer, "{}", *field); 273 return this.format_buffer; 274 } 275 276 277 /*************************************************************************** 278 279 Adds a cell to the current row. 280 281 ***************************************************************************/ 282 283 private void addCell ( cstring str ) 284 { 285 this.cells ~= Table.Row.Cell.String(str); 286 } 287 288 289 /*************************************************************************** 290 291 Template to mix in a call to the addCell() method for each field of a 292 struct. It is assumed that an instance of the struct S exists in scope 293 with the name 'item'. 294 295 ***************************************************************************/ 296 297 private template ContentsRow ( size_t i = 0 ) 298 { 299 static if ( i == S.tupleof.length ) 300 { 301 static immutable istring ContentsRow = ""; 302 } 303 else 304 { 305 static immutable istring ContentsRow = "this.addCell(this." 306 ~ identifier!(S.tupleof[i]) 307 ~ "_string(&item.tupleof[" ~ i.stringof ~ "]));" 308 ~ ContentsRow!(i + 1); 309 } 310 } 311 312 313 /*************************************************************************** 314 315 Template to mix in a call to the addCell() method for each field of a 316 struct. The names of the struct's fields are added as headers for each 317 column in the table. 318 319 ***************************************************************************/ 320 321 private template HeaderRow ( size_t i = 0 ) 322 { 323 static if ( i == S.tupleof.length ) 324 { 325 static immutable istring HeaderRow = ""; 326 } 327 else 328 { 329 static immutable istring HeaderRow = `this.addCell("` 330 ~ identifier!(S.tupleof[i]) 331 ~ `");` ~ HeaderRow!(i + 1); 332 } 333 } 334 } 335 336 unittest 337 { 338 struct Entry 339 { 340 int field; 341 double field2; 342 } 343 344 alias StructTable!(Entry) Instance; 345 }