ToText_TableDebugView.cpp
Go to the documentation of this file.
2 
3 #include "TextDebugView.hpp"
4 
5 #include "../Basics/StringIOIndex.hpp"
6 #include "../Basics/utils.hpp"
7 #include "../Instancing/Instance.hpp"
8 
9 #include <map>
10 
11 namespace iv
12 {
13 
15  TableDebugView( context )
16 {
17 }
18 
19 std::string ToText_TableDebugView::write( std::any const & value )
20 {
21  return ::iv::StringIOIndex::Write( value, this->context() );
22 }
23 
24 void ToText_TableDebugView::Write_AsLines( TextDebugView * out, bool print_table_names )
25 {
26  std::unordered_map< TableId, TableData > const & tables = this->Tables();
27 
28  size_t max_width = 0;
29 
30  // compute base width
31  for( auto & [ table_id, table ] : tables )
32  for( auto & row : table.rows )
33  for( auto & [ column_name, column ] : row.columns )
34  {
35  (void)table_id;
36  (void)column;
37  std::string base_str = column_name + ": ";
38  max_width = std::max( max_width, utf8_size( base_str ) );
39 
40  for( size_t i = 0; i < column.hints.size(); i++ )
41  {
42  std::string const & hint_name = column.hints[ i ].name;
43  std::string base_str = " " + hint_name + ": ";
44  max_width = std::max( max_width, utf8_size( base_str ) );
45  }
46  }
47 
48  // print
49  for( auto & [ table_id, table ] : tables )
50  for( auto & row : table.rows )
51  {
52  if( print_table_names )
53  out->out() << "-" << table_id.persistent_value() << "-" << std::endl;
54 
55  for( auto & [ column_name, column ] : row.columns )
56  {
57  // create name string
58  std::string base_str;
59  //base_str += table_id.persistent_value() + "::" + column_name + ": ";
60  base_str += column_name + ": ";
61 
62  // print name string and whitespace fill
63  out->out() << base_str;
64  for( size_t i = utf8_size( base_str ); i < max_width; i++ )
65  out->out() << " ";
66 
67  // print column value
68  std::string pref = "";
69  for( size_t i = 0; i < max_width; i++ )
70  pref += " ";
71  out->prefix_push( pref.c_str() );
72  out->out() << this->write( column.value );
73  out->prefix_pop();
74 
75  //
76  out->out() << std::endl;
77 
78  // print hints
79  for( size_t i = 0; i < column.hints.size(); i++ )
80  {
81  std::string const & hint_name = column.hints[ i ].name;
82  std::any const & hint_val = column.hints[ i ].hint;
83 
84  std::string base_str = " " + hint_name + ": ";
85 
86  out->out() << base_str;
87  for( size_t i = utf8_size( base_str ); i < max_width; i++ )
88  out->out() << " ";
89 
90  out->out() << this->write( hint_val );
91  out->out() << std::endl;
92  }
93  }
94 
95  if( print_table_names )
96  out->out() << std::endl;
97  }
98 }
99 
101 {
102  std::unordered_map< TableId, TableData > const & tables = this->Tables();
103 
104  std::map< std::string, size_t > columns; // currently ordered alphabeticaly by column name; we might want to somehow retrieve aggregate order in which columns were written and use that order; or use some explicit ordering
105  for( auto & [ table_id, table ] : tables )
106  {
107  //---- fetch columns and their widths ----
108  columns.clear();
109  for( auto & row : table.rows )
110  {
111  for( auto & [ column_name, column ] : row.columns )
112  {
113  size_t & line_size = columns[ column_name ];
114 
115  // adjust for column header
116  line_size = std::max( line_size, utf8_size( column_name ) + 4 );
117 
118  // adjust for column content
119  std::string strcol = this->write( column.value );
120  line_size = std::max( line_size, utf8_size( strcol ) + 2 );
121 
122  // adjust for hints
123  for( auto & [ hint_name, hint ] : column.hints )
124  {
125  std::string strhint = this->write( hint );
126  size_t hintsize = 4 + utf8_size( hint_name ) + 2 + utf8_size( this->write( hint ) ) + 1;
127  line_size = std::max( line_size, hintsize );
128  }
129  }
130  }
131 
132  // add at least one column so that it is easier to write (it will not be filled with anything anyway)
133  if( columns.empty() )
134  columns[ "" ] = 0;
135 
136  // adjust last column width for table title
137  size_t title_req_width = 4 + utf8_size( table_id.persistent_value() ) + 2;
138  size_t total_width = 1;
139  for( auto & [ colname, colwidth ] : columns )
140  {
141  (void)colname;
142  total_width += colwidth + 1;
143  }
144 
145  if( total_width < title_req_width )
146  {
147  if( columns.empty() )
148  {
149  columns[ "" ] = 6;
150  }
151  else
152  {
153  size_t & last_col_width = columns.rbegin()->second;
154  last_col_width += title_req_width - total_width;
155  }
156  }
157 
158  //-------------- helpers ----------------------
159  auto write_vertical_separator = [&]( const char * left, const char * separator, const char * right, const char * filler )
160  {
161  bool first = true;
162  context->out() << left;
163  for( auto & [ colname, colwidth ] : columns )
164  {
165  (void)colname;
166  if( first )
167  first = false;
168  else
169  context->out() << separator;
170 
171  for( size_t i = 0; i < colwidth; i++ )
172  context->out() << filler;
173  }
174  context->out() << right << std::endl;
175  };
176 
177  auto write_table_title = [&]( std::string const & title )
178  {
179  //
180  size_t i = 0;
181  auto write_symbol = [&]( const char * symbol )
182  {
183  const size_t offset = 3;
184  if( i < offset )
185  context->out() << symbol;
186  else if( i == offset )
187  context->out() << "╸";
188  else if( i <= offset + title.size() )
189  context->out() << title[ i - offset - 1 ];
190  else if( i <= offset + title.size() + 1 )
191  context->out() << "╺";
192  else
193  context->out() << symbol;
194  i++;
195  };
196 
197  //
198  bool first = true;
199  write_symbol( "┏" );
200  for( auto & [ colname, colwidth ] : columns )
201  {
202  (void)colname;
203  if( first )
204  first = false;
205  else
206  write_symbol( "┯" );
207 
208  for( size_t i = 0; i < colwidth; i++ )
209  write_symbol( "━" );
210  }
211  write_symbol( "┓" );
212  context->out() << std::endl;
213  };
214 
215  auto write_column_titles = [&]()
216  {
217  bool first = true;
218  context->out() << "┃";
219  for( auto & [ colname, colwidth ] : columns )
220  {
221  if( first )
222  first = false;
223  else
224  context->out() << "│";
225 
226  context->out() << " -" << colname << "- ";
227 
228  for( size_t i = utf8_size( colname ) + 4; i < colwidth; i++ )
229  context->out() << ' ';
230  }
231  context->out() << "┃" << std::endl;
232  };
233 
234  auto write_column_values = [&]( table_debug_context::RowData const & row ) -> bool // returns true if some column has some hints, false if no column has any hints
235  {
236  bool result = false;
237 
238  bool first = true;
239  context->out() << "┃";
240  for( auto & [ colname, colwidth ] : columns )
241  {
242  if( first )
243  first = false;
244  else
245  context->out() << "│";
246 
247  auto it_coldata = row.columns.find( colname );
248  if( it_coldata != row.columns.end() )
249  {
250  table_debug_context::ColumnData const & coldata = it_coldata->second;
251  if( coldata.hints.size() )
252  result = true;
253 
254  std::string coldata_str = this->write( coldata.value );
255  context->out() << ' ' << coldata_str << ' ';
256  for( size_t i = utf8_size( coldata_str ) + 2; i < colwidth; i++ )
257  context->out() << ' ';
258  }
259  else
260  {
261  for( size_t i = 0; i < colwidth; i++ )
262  context->out() << ' ';
263  }
264  }
265  context->out() << "┃" << std::endl;
266 
267  return result;
268  };
269 
270  auto write_column_hints = [&]( table_debug_context::RowData const & row, size_t hint_id ) -> bool // returns true if some column has a hint on position hint_id+1 (next hint)
271  {
272  bool result = false;
273 
274  bool first = true;
275  context->out() << "┃";
276  for( auto & [ colname, colwidth ] : columns )
277  {
278  if( first )
279  first = false;
280  else
281  context->out() << "│";
282 
283  auto it_coldata = row.columns.find( colname );
284  if( it_coldata != row.columns.end() )
285  {
286  table_debug_context::ColumnData const & coldata = it_coldata->second;
287  if( coldata.hints.size() > hint_id )
288  {
289  // check if it is last hint
290  bool last = !( coldata.hints.size() > hint_id + 1 );
291  if( !last )
292  result = true;
293 
294  // retrieve hint data
295  auto & [ hint_name, hint_any ] = coldata.hints[ hint_id ];
296  std::string hint_str = this->write( hint_any );
297 
298  // write hint
299  size_t i = 0;
300  if( last )
301  context->out() << " └╴";
302  else
303  context->out() << " ├╴";
304  i += 4;
305 
306  if( !hint_name.empty() )
307  {
308  context->out() << hint_name << ": ";
309  i += utf8_size( hint_name ) + 2;
310  }
311 
312  context->out() << hint_str << ' ';
313  i += utf8_size( hint_str ) + 1;
314 
315  // fill with spaces
316  for( ; i < colwidth; i++ )
317  context->out() << ' ';
318  }
319  else
320  {
321  for( size_t i = 0; i < colwidth; i++ )
322  context->out() << ' ';
323  }
324  }
325  else
326  {
327  for( size_t i = 0; i < colwidth; i++ )
328  context->out() << ' ';
329  }
330  }
331 
332  context->out() << "┃" << std::endl;
333 
334  return result;
335  };
336 
337  //-------- print table --------
338  // top line
339  write_table_title( table_id.persistent_value() );
340 
341  // colnames
342  write_column_titles();
343 
344  // header - body separator
345  write_vertical_separator( "┣", "┿", "┫", "━" );
346 
347  // rows
348  bool first_row = true;
349  for( auto const & row : table.rows )
350  {
351  // row separator
352  if( first_row )
353  first_row = false;
354  else
355  write_vertical_separator( "┠", "┼", "┨", "─" );
356 
357  // column values and hints
358  if( write_column_values( row ) )
359  {
360  // column hints
361  for( size_t i = 0; write_column_hints( row, i ); i++ );
362  }
363  }
364 
365  // table ending line
366  write_vertical_separator( "┗", "┷", "┛", "━" );
367  }
368 }
369 
370 }