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 }