File Coverage

blib/lib/Text/Table/Any.pm
Criterion Covered Total %
statement 26 245 10.6
branch 19 192 9.9
condition 3 9 33.3
subroutine 7 9 77.7
pod 2 2 100.0
total 57 457 12.4


line stmt bran cond sub pod time code
1              
2             use 5.010001;
3 1     1   86337 use strict;
  1         11  
4 1     1   6 use warnings;
  1         2  
  1         20  
5 1     1   3  
  1         2  
  1         31  
6             use Exporter qw(import);
7 1     1   5 our @EXPORT_OK = qw(generate_table);
  1         1  
  1         2889  
8              
9             our $AUTHORITY = 'cpan:PERLANCAR'; # AUTHORITY
10             our $DATE = '2022-05-27'; # DATE
11             our $DIST = 'Text-Table-Any'; # DIST
12             our $VERSION = '0.114'; # VERSION
13              
14             our %BACKEND_FEATURES = (
15             "Term::Table" => {
16             rows => 1,
17             header_row => 1,
18             separate_rows => 0,
19             caption => 0,
20             align => 0,
21             },
22             "Term::TablePrint" => {
23             rows => 1,
24             header_row => 1,
25             separate_rows => 0,
26             caption => 0,
27             align => 0,
28             },
29             "Text::ANSITable" => {
30             rows => 1,
31             header_row => 1,
32             separate_rows => 1,
33             caption => 0,
34             align => 1,
35             },
36             "Text::ASCIITable" => {
37             rows => 1,
38             header_row => 1,
39             separate_rows => 0,
40             caption => 0,
41             align => 1,
42             },
43             "Text::FormatTable" => {
44             rows => 1,
45             header_row => 0,
46             separate_rows => 0,
47             caption => 0,
48             align => 1,
49             align_note => "c(enter) alignment is not supported, will fallback to l(eft)",
50             },
51             "Text::MarkdownTable" => {
52             rows => 1,
53             header_row => 1,
54             separate_rows => 0,
55             caption => 0,
56             align => 0,
57             },
58             "Text::Table" => {
59             rows => 1,
60             header_row => 0,
61             separate_rows => 0,
62             caption => 0,
63             align => 1,
64             },
65             "Text::Table::ASV" => {
66             rows => 1,
67             header_row => 1,
68             separate_rows => 0,
69             caption => 0,
70             backend_opts => 1,
71             backend_opts_note => "Backend-specific options (backend_opts) will be passed to table() or generate_table() directly",
72             align => 0,
73             },
74             "Text::Table::CSV" => {
75             rows => 1,
76             header_row => 1,
77             separate_rows => 0,
78             caption => 0,
79             backend_opts => 1,
80             backend_opts_note => "Backend-specific options (backend_opts) will be passed to table() or generate_table() directly",
81             align => 0,
82             },
83             "Text::Table::HTML" => {
84             rows => 1,
85             header_row => 1,
86             separate_rows => 0,
87             caption => 1,
88             backend_opts => 1,
89             backend_opts_note => "Backend-specific options (backend_opts) will be passed to table() or generate_table() directly",
90             align => 0,
91             align_note => "TODO, backend does not support yet, parameter already passed",
92             },
93             "Text::Table::HTML::DataTables" => {
94             rows => 1,
95             header_row => 1,
96             separate_rows => 0,
97             caption => 1,
98             backend_opts => 1,
99             backend_opts_note => "Backend-specific options (backend_opts) will be passed to table() or generate_table() directly",
100             align => 0,
101             align_note => "TODO, backend does not support yet, parameter already passed",
102             },
103             "Text::Table::LTSV" => {
104             rows => 1,
105             header_row => 0,
106             separate_rows => 0,
107             caption => 0,
108             backend_opts => 1,
109             backend_opts_note => "Backend-specific options (backend_opts) will be passed to table() or generate_table() directly",
110             align => 0,
111             },
112             "Text::Table::Manifold" => {
113             rows => 1,
114             header_row => 1,
115             separate_rows => 0,
116             caption => 0,
117             align => 1,
118             },
119             "Text::Table::More" => {
120             rows => 1,
121             header_row => 1,
122             separate_rows => 1,
123             caption => 0,
124             backend_opts => 1,
125             backend_opts_note => "Backend-specific options (backend_opts) will be passed to table() or generate_table() directly",
126             align => 1,
127             },
128             "Text::Table::Org" => {
129             rows => 1,
130             header_row => 1,
131             separate_rows => 1,
132             caption => 0,
133             backend_opts => 1,
134             backend_opts_note => "Backend-specific options (backend_opts) will be passed to table() or generate_table() directly",
135             align => 0,
136             },
137             "Text::Table::Paragraph" => {
138             rows => 1,
139             header_row => 1,
140             separate_rows => 0,
141             caption => 0,
142             backend_opts => 1,
143             backend_opts_note => "Backend-specific options (backend_opts) will be passed to table() or generate_table() directly",
144             align => 0,
145             },
146             "Text::Table::Sprintf" => {
147             rows => 1,
148             header_row => 1,
149             separate_rows => 1,
150             caption => 0,
151             backend_opts => 1,
152             backend_opts_note => "Backend-specific options (backend_opts) will be passed to table() or generate_table() directly",
153             align => 0,
154             align_note => "",
155             },
156             "Text::Table::TickitWidget" => {
157             rows => 1,
158             header_row => 1,
159             separate_rows => 0,
160             caption => 0,
161             backend_opts => 1,
162             backend_opts_note => "Backend-specific options (backend_opts) will be passed to table() or generate_table() directly",
163             align => 0,
164             },
165             "Text::Table::Tiny" => {
166             rows => 1,
167             header_row => 1,
168             separate_rows => 1,
169             caption => 0,
170             backend_opts => 1,
171             backend_opts_note => "Backend-specific options (backend_opts) will be passed to table() or generate_table() directly",
172             align => 1,
173             },
174             "Text::Table::TinyBorderStyle" => {
175             rows => 1,
176             header_row => 1,
177             separate_rows => 1,
178             caption => 0,
179             backend_opts => 1,
180             backend_opts_note => "Backend-specific options (backend_opts) will be passed to table() or generate_table() directly",
181             align => 0,
182             align_note => "TODO, backend does not support yet, parameter already passed",
183             },
184             "Text::Table::TinyColor" => {
185             rows => 1,
186             header_row => 1,
187             separate_rows => 1,
188             caption => 0,
189             backend_opts => 1,
190             backend_opts_note => "Backend-specific options (backend_opts) will be passed to table() or generate_table() directly",
191             align => 0,
192             align_note => "TODO, backend does not support yet, parameter already passed",
193             },
194             "Text::Table::TinyColorWide" => {
195             rows => 1,
196             header_row => 1,
197             separate_rows => 1,
198             caption => 0,
199             backend_opts => 1,
200             backend_opts_note => "Backend-specific options (backend_opts) will be passed to table() or generate_table() directly",
201             align => 0,
202             align_note => "TODO, backend does not support yet, parameter already passed",
203             },
204             "Text::Table::TinyWide" => {
205             rows => 1,
206             header_row => 1,
207             separate_rows => 1,
208             caption => 0,
209             backend_opts => 1,
210             backend_opts_note => "Backend-specific options (backend_opts) will be passed to table() or generate_table() directly",
211             align => 0,
212             align_note => "TODO, backend does not support yet, parameter already passed",
213             },
214             "Text::Table::TSV" => {
215             rows => 1,
216             header_row => 0,
217             separate_rows => 0,
218             caption => 0,
219             backend_opts => 1,
220             backend_opts_note => "Backend-specific options (backend_opts) will be passed to table() or generate_table() directly",
221             align => 0,
222             },
223             "Text::Table::XLSX" => {
224             rows => 1,
225             header_row => 1,
226             separate_rows => 0,
227             caption => 0,
228             backend_opts => 1,
229             backend_opts_note => "Backend-specific options (backend_opts) will be passed to table() or generate_table() directly",
230             align => 0,
231             },
232             "Text::TabularDisplay" => {
233             rows => 1,
234             header_row => 0,
235             separate_rows => 0,
236             caption => 0,
237             align => 0,
238             },
239             "Text::UnicodeBox::Table" => {
240             rows => 1,
241             header_row => 1,
242             separate_rows => 0,
243             caption => 0,
244             align => 0,
245             align_note => "TODO: backend supports left/right",
246             },
247             );
248              
249             our @BACKENDS = sort keys %BACKEND_FEATURES;
250              
251             my $val = shift;
252             $val =~ s/([\\"])/\\$1/g;
253 0     0   0 "\"$val\"";
254 0         0 }
255 0         0  
256             @BACKENDS;
257             }
258              
259 0     0 1 0 my %params = @_;
260              
261             my $rows = $params{rows} or die "Must provide rows!";
262             my $backend = $params{backend} || 'Text::Table::Sprintf';
263 2     2 1 4087 my $header_row = $params{header_row} // 0;
264             my $separate_rows = $params{separate_rows} // 0;
265 2 50       7 my $align = $params{align};
266 2   50     8  
267 2   50     6 if ($backend eq 'Term::Table') {
268 2   50     6 require Term::Table;
269 2         3 my ($header, $data_rows);
270             if ($header_row) {
271 2 50       22 $header = $rows->[0];
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
272 0         0 $data_rows = [ @{$rows}[1 .. $#{$rows}] ];
273 0         0 } else {
274 0 0       0 $header = [ map {"col$_"} 0..$#{$rows->[0]} ];
275 0         0 $data_rows = $rows;
276 0         0 }
  0         0  
  0         0  
277             my $table = Term::Table->new(
278 0         0 header => $header,
  0         0  
  0         0  
279 0         0 rows => $data_rows,
280             );
281 0         0 return join("\n", $table->render)."\n";
282             } elsif ($backend eq 'Term::TablePrint') {
283             require Term::TablePrint;
284             my $rows2;
285 0         0 if ($header_row) {
286             $rows2 = $rows;
287 0         0 } else {
288 0         0 $rows2 = [@$rows];
289 0 0       0 shift @$rows2;
290 0         0 }
291             return Term::TablePrint::print_table($rows);
292 0         0 } elsif ($backend eq 'Text::ANSITable') {
293 0         0 require Text::ANSITable;
294             my $t = Text::ANSITable->new(
295 0         0 use_utf8 => 0,
296             use_box_chars => 0,
297 0         0 use_color => 0,
298 0 0 0     0 border_style => 'ASCII::SingleLine',
    0          
    0          
299             ($align && !ref($align) ? (cell_align => ($align eq 'r' ? 'right' : $align eq 'c' ? 'middle' : 'left')) : ()),
300             );
301             # XXX pick an appropriate border style when header_row=0
302             if ($header_row) {
303             $t->columns($rows->[0]);
304             $t->add_row($rows->[$_]) for 1..@$rows-1;
305             } else {
306 0 0       0 $t->columns([ map {"col$_"} 0..$#{$rows->[0]} ]);
307 0         0 $t->add_row($_) for @$rows;
308 0         0 }
309             $t->show_row_separator(1) if $separate_rows;
310 0         0 if (ref $align) {
  0         0  
  0         0  
311 0         0 for my $i (0 .. @{$rows->[0]}-1) {
312             my $col_align = $align->[$i];
313 0 0       0 next unless $col_align;
314 0 0       0 $t->set_column_style($i, align => ($col_align eq 'r' ? 'right' : $col_align eq 'c' ? 'middle' : 'left'));
315 0         0 }
  0         0  
316 0         0 }
317 0 0       0 return $t->draw;
318 0 0       0 } elsif ($backend eq 'Text::ASCIITable') {
    0          
319             require Text::ASCIITable;
320             my $t = Text::ASCIITable->new();
321 0         0 my @colnames;
322             if ($header_row) {
323 0         0 @colnames = @{ $rows->[0] };
324 0         0 $t->setCols(@colnames);
325 0         0 $t->addRow(@{ $rows->[$_] }) for 1..@$rows-1;
326 0 0       0 } else {
327 0         0 @colnames = map { "col$_" } 0..$#{ $rows->[0] };
  0         0  
328 0         0 $t->setCols(@colnames);
329 0         0 $t->addRow(@$_) for @$rows;
  0         0  
330             }
331 0         0 if ($align) {
  0         0  
  0         0  
332 0         0 for my $i (ref $align ? (0 .. $#{$align}) : (0 .. @colnames-1)) {
333 0         0 my $colname = $colnames[$i];
334             my $col_align = ref $align ? $align->[$i] : $align;
335 0 0       0 my $align_val = ($col_align eq 'r' ? 'right' : $col_align eq 'c' ? 'center' : 'left');
336 0 0       0 #say "D:aligning col: $colname -> $align_val";
  0         0  
337 0         0 $t->alignCol($colname, $align_val);
338 0 0       0 }
339 0 0       0 }
    0          
340             return "$t";
341 0         0 } elsif ($backend eq 'Text::FormatTable') {
342             require Text::FormatTable;
343             my @formats = ('l') x @{ $rows->[0] };
344 0         0 if ($align) {
345             if (ref $align) {
346 0         0 for my $i (0 .. @{$align}-1) {
347 0         0 my $col_align = $align->[$i];
  0         0  
348 0 0       0 $formats[$i] = $col_align eq 'r' ? 'r' : 'l';
349 0 0       0 }
350 0         0 } else {
  0         0  
351 0         0 @formats = ($align eq 'r' ? 'r' : 'l') x @{ $rows->[0] };
352 0 0       0 }
353             }
354             #use DD; dd \@formats;
355 0 0       0 my $t = Text::FormatTable->new(join('|', @formats));
  0         0  
356             $t->head(@{ $rows->[0] });
357             $t->row(@{ $rows->[$_] }) for 1..@$rows-1;
358             return $t->render;
359 0         0 } elsif ($backend eq 'Text::MarkdownTable') {
360 0         0 require Text::MarkdownTable;
  0         0  
361 0         0 my $out = "";
  0         0  
362 0         0 my $fields = $header_row ?
363             $rows->[0] : [map {"col$_"} 0..$#{ $rows->[0] }];
364 0         0 my $t = Text::MarkdownTable->new(file => \$out, columns => $fields);
365 0         0 foreach (($header_row ? 1:0) .. $#{$rows}) {
366             my $row = $rows->[$_];
367 0 0       0 $t->add( {
  0         0  
  0         0  
368 0         0 map { $fields->[$_] => $row->[$_] } 0..@$fields-1
369 0 0       0 });
  0         0  
370 0         0 }
371             $t->done;
372 0         0 return $out;
  0         0  
373             } elsif ($backend eq 'Text::Table') {
374             require Text::Table;
375 0         0 my @colspecs;
376 0         0 for my $i (0 .. @{ $rows->[0] }-1) {
377             push @colspecs, {
378 0         0 title => $rows->[0][$i],
379 0         0 };
380 0         0 }
  0         0  
381 0         0 if ($align) {
382             if (ref $align) {
383             for my $i (0 .. @{$align}-1) {
384             my $col_align = $align->[$i];
385 0 0       0 $colspecs[$i]{align} = $col_align eq 'r' ? 'right' : $col_align eq 'c' ? 'center' : 'left';
386 0 0       0 $colspecs[$i]{align_title} = $colspecs[$i]{align};
387 0         0 }
  0         0  
388 0         0 } else {
389 0 0       0 for my $i (0 .. @{ $rows->[0] }-1) {
    0          
390 0         0 $colspecs[$i]{align} = $align eq 'r' ? 'right' : $align eq 'c' ? 'center' : 'left';
391             $colspecs[$i]{align_title} = $colspecs[$i]{align};
392             }
393 0         0 }
  0         0  
394 0 0       0 }
    0          
395 0         0 #use DD; dd \@colspecs;
396             my $t = Text::Table->new(@colspecs);
397             $t->load(@{ $rows }[1..@$rows-1]);
398             return "$t";
399             } elsif ($backend eq 'Text::Table::ASV') {
400 0         0 require Text::Table::ASV;
401 0         0 return Text::Table::ASV::table(
  0         0  
402 0         0 rows => $rows,
403             header_row => $header_row,
404 0         0 defined($params{backend_opts}) ? %{$params{backend_opts}} : (),
405             );
406             } elsif ($backend eq 'Text::Table::CSV') {
407             require Text::Table::CSV;
408 0 0       0 return Text::Table::CSV::table(
  0         0  
409             rows => $rows,
410             header_row => $header_row,
411 0         0 defined($params{backend_opts}) ? %{$params{backend_opts}} : (),
412             );
413             } elsif ($backend eq 'Text::Table::HTML') {
414             require Text::Table::HTML;
415 0 0       0 return Text::Table::HTML::table(
  0         0  
416             rows => $rows,
417             header_row => $header_row,
418 0         0 (caption => $params{caption}) x !!defined($params{caption}),
419             defined($params{backend_opts}) ? %{$params{backend_opts}} : (),
420             ($align ? (align => $align) : ()),
421             );
422             } elsif ($backend eq 'Text::Table::HTML::DataTables') {
423 0 0       0 require Text::Table::HTML::DataTables;
  0 0       0  
424             return Text::Table::HTML::DataTables::table(
425             rows => $rows,
426             header_row => $header_row,
427 0         0 (caption => $params{caption}) x !!defined($params{caption}),
428             defined($params{backend_opts}) ? %{$params{backend_opts}} : (),
429             ($align ? (align => $align) : ()),
430             );
431             } elsif ($backend eq 'Text::Table::LTSV') {
432 0 0       0 require Text::Table::LTSV;
  0 0       0  
433             return Text::Table::LTSV::table(
434             rows => $rows,
435             defined($params{backend_opts}) ? %{$params{backend_opts}} : (),
436 0         0 );
437             } elsif ($backend eq 'Text::Table::Manifold') {
438             require Text::Table::Manifold;
439 0 0       0 my @ttm_args;
  0         0  
440             if ($align) {
441             my @aligns;
442 0         0 if (ref $align) {
443 0         0 for my $i (0 .. @{$align}-1) {
444 0 0       0 my $col_align = $align->[$i];
445 0         0 push @aligns, $col_align eq 'r' ? Text::Table::Manifold::align_right() : $col_align eq 'c' ? Text::Table::Manifold::align_center() : Text::Table::Manifold::align_left();
446 0 0       0 }
447 0         0 } else {
  0         0  
448 0         0 for my $i (0 .. @{ $rows->[0] }-1) {
449 0 0       0 my $col_align = $align;
    0          
450             push @aligns, $col_align eq 'r' ? Text::Table::Manifold::align_right() : $col_align eq 'c' ? Text::Table::Manifold::align_center() : Text::Table::Manifold::align_left();
451             }
452 0         0 }
  0         0  
453 0         0 push @ttm_args, alignment => \@aligns;
454 0 0       0 }
    0          
455             my $t = Text::Table::Manifold->new(@ttm_args);
456             if ($header_row) {
457 0         0 $t->headers($rows->[0]);
458             $t->data([ @{$rows}[1 .. $#{$rows}] ]);
459 0         0 } else {
460 0 0       0 $t->headers([ map {"col$_"} 0..$#{$rows->[0]} ]);
461 0         0 $t->data($rows);
462 0         0 }
  0         0  
  0         0  
463             return join("\n", @{$t->render(padding => 1)}) . "\n";
464 0         0 } elsif ($backend eq 'Text::Table::More') {
  0         0  
  0         0  
465 0         0 require Text::Table::More;
466             my @ttm_args = (
467 0         0 rows => $rows,
  0         0  
468             header_row => $header_row,
469 0         0 separate_rows => $separate_rows,
470             defined($params{backend_opts}) ? %{$params{backend_opts}} : (),
471             );
472             if ($align) {
473             if (ref $align) {
474 0 0       0 my @col_attrs;
  0         0  
475             for my $i (0 .. @$align-1) {
476 0 0       0 my $col_align = $align->[$i];
477 0 0       0 push @col_attrs, [$i, {align=>($col_align eq 'r' ? 'right' : $col_align eq 'c' ? 'middle' : 'left')}];
478 0         0 }
479 0         0 push @ttm_args, col_attrs => \@col_attrs;
480 0         0 } else {
481 0 0       0 push @ttm_args, align => ($align eq 'r' ? 'right' : $align eq 'c' ? 'middle' : 'left');
    0          
482             }
483 0         0 }
484             return Text::Table::More::generate_table(@ttm_args);
485 0 0       0 } elsif ($backend eq 'Text::Table::Org') {
    0          
486             require Text::Table::Org;
487             return Text::Table::Org::table(
488 0         0 rows => $rows,
489             header_row => $header_row,
490 0         0 separate_rows => $separate_rows,
491             defined($params{caption}) ? (caption => $params{caption}) : (),
492             defined($params{backend_opts}) ? %{$params{backend_opts}} : (),
493             );
494             } elsif ($backend eq 'Text::Table::Paragraph') {
495             require Text::Table::Paragraph;
496 0 0       0 return Text::Table::Paragraph::table(
  0 0       0  
497             rows => $rows,
498             header_row => $header_row,
499 0         0 defined($params{backend_opts}) ? %{$params{backend_opts}} : (),
500             );
501             } elsif ($backend eq 'Text::Table::Sprintf') {
502             require Text::Table::Sprintf;
503 0 0       0 return Text::Table::Sprintf::table(
  0         0  
504             rows => $rows,
505             header_row => $header_row,
506 2         765 separate_rows => $separate_rows,
507             defined($params{backend_opts}) ? %{$params{backend_opts}} : (),
508             );
509             } elsif ($backend eq 'Text::Table::TickitWidget') {
510             require Text::Table::TickitWidget;
511 2 50       44 return Text::Table::TickitWidget::table(
  0            
512             rows => $rows,
513             header_row => $header_row,
514 0           defined($params{backend_opts}) ? %{$params{backend_opts}} : (),
515             ) . "\n";
516             } elsif ($backend eq 'Text::Table::Tiny') {
517             require Text::Table::Tiny;
518 0 0         return Text::Table::Tiny::table(
  0            
519             rows => $rows,
520             header_row => $header_row,
521 0           separate_rows => $separate_rows,
522             defined($params{backend_opts}) ? %{$params{backend_opts}} : (),
523             $align ? (align => $align) : (),
524             ) . "\n";
525             } elsif ($backend eq 'Text::Table::TinyBorderStyle') {
526 0 0         require Text::Table::TinyBorderStyle;
  0 0          
527             return Text::Table::TinyBorderStyle::table(
528             rows => $rows,
529             header_row => $header_row,
530 0           separate_rows => $separate_rows,
531             defined($params{backend_opts}) ? %{$params{backend_opts}} : (),
532             ($align ? (align => $align) : ()),
533             ) . "\n";
534             } elsif ($backend eq 'Text::Table::TinyColor') {
535 0 0         require Text::Table::TinyColor;
  0 0          
536             return Text::Table::TinyColor::table(
537             rows => $rows,
538             header_row => $header_row,
539 0           separate_rows => $separate_rows,
540             defined($params{backend_opts}) ? %{$params{backend_opts}} : (),
541             ($align ? (align => $align) : ()),
542             ) . "\n";
543             } elsif ($backend eq 'Text::Table::TinyColorWide') {
544 0 0         require Text::Table::TinyColorWide;
  0 0          
545             return Text::Table::TinyColorWide::table(
546             rows => $rows,
547             header_row => $header_row,
548 0           separate_rows => $separate_rows,
549             defined($params{backend_opts}) ? %{$params{backend_opts}} : (),
550             ($align ? (align => $align) : ()),
551             ) . "\n";
552             } elsif ($backend eq 'Text::Table::TinyWide') {
553 0 0         require Text::Table::TinyWide;
  0 0          
554             return Text::Table::TinyWide::table(
555             rows => $rows,
556             header_row => $header_row,
557 0           separate_rows => $separate_rows,
558             defined($params{backend_opts}) ? %{$params{backend_opts}} : (),
559             ($align ? (align => $align) : ()),
560             ) . "\n";
561             } elsif ($backend eq 'Text::Table::TSV') {
562 0 0         require Text::Table::TSV;
  0 0          
563             return Text::Table::TSV::table(
564             rows => $rows,
565             defined($params{backend_opts}) ? %{$params{backend_opts}} : (),
566 0           );
567             } elsif ($backend eq 'Text::Table::XLSX') {
568             require Text::Table::XLSX;
569 0 0         return Text::Table::XLSX::table(
  0            
570             rows => $rows,
571             header_row => $header_row,
572 0           defined($params{backend_opts}) ? %{$params{backend_opts}} : (),
573             );
574             } elsif ($backend eq 'Text::TabularDisplay') {
575             require Text::TabularDisplay;
576 0 0         my $t = Text::TabularDisplay->new(@{ $rows->[0] });
  0            
577             $t->add(@{ $rows->[$_] }) for 1..@$rows-1;
578             return $t->render . "\n";
579 0           } elsif ($backend eq 'Text::UnicodeBox::Table') {
580 0           require Text::UnicodeBox::Table;
  0            
581 0           my $t = Text::UnicodeBox::Table->new;
  0            
582 0           if ($header_row) {
583             $t->add_header(@{ $rows->[0] });
584 0           $t->add_row(@{ $rows->[$_] }) for 1 .. $#{$rows};
585 0           } else {
586 0 0         $t->add_header(map {"col$_"} 0..$#{$rows->[0]});
587 0           $t->add_row(@{ $rows->[$_] }) for 0 .. $#{$rows};
  0            
588 0           }
  0            
  0            
589             return $t->render;
590 0           } else {
  0            
  0            
591 0           die "Unknown backend '$backend'";
  0            
  0            
592             }
593 0           }
594              
595 0           {
596             no strict 'refs'; ## no critic: TestingAndDebugging::ProhibitNoStrict
597             no warnings 'once';
598             *table = \&generate_table;
599             }
600 1     1   7  
  1         2  
  1         50  
601 1     1   6 1;
  1         2  
  1         70  
602             # ABSTRACT: Generate text table using one of several backends
603              
604              
605             =pod
606              
607             =encoding UTF-8
608              
609             =head1 NAME
610              
611             Text::Table::Any - Generate text table using one of several backends
612              
613             =head1 VERSION
614              
615             This document describes version 0.114 of Text::Table::Any (from Perl distribution Text-Table-Any), released on 2022-05-27.
616              
617             =head1 SYNOPSIS
618              
619             use Text::Table::Any qw/generate_table/;
620              
621             my $rows = [
622             # first element is header row
623             ['Distribution', 'Author', 'First Version', 'Latest Version', 'Abstract'],
624              
625             # subsequent elements are data rows
626             ['ACME-Dzil-Test-daemon', 'DAEMON', '0.001', '0.001', 'Module abstract placeholder text'],
627             ['ACME-Dzil-Test-daemon2', 'DAEMON', '0.001', '0.001', 'Module abstract placeholder text'],
628             ['Acme-CPANModules-ShellCompleters', 'PERLANCAR', '0.001', '0.001', 'Modules that provide shell tab completion for other commands/scripts'],
629             ['Acme-CPANModules-WorkingWithURL', 'PERLANCAR', '0.001', '0.001', 'Working with URL'],
630             ];
631              
632             print generate_table(rows => $rows);
633              
634             will render the table using the default backend L<Text::Table::Sprintf> and
635             print something like:
636              
637             +----------------------------------+-----------+---------------+----------------+----------------------------------------------------------------------+
638             | Distribution | Author | First Version | Latest Version | Abstract |
639             +----------------------------------+-----------+---------------+----------------+----------------------------------------------------------------------+
640             | ACME-Dzil-Test-daemon | DAEMON | 0.001 | 0.001 | Module abstract placeholder text |
641             | ACME-Dzil-Test-daemon2 | DAEMON | 0.001 | 0.001 | Module abstract placeholder text |
642             | Acme-CPANModules-ShellCompleters | PERLANCAR | 0.001 | 0.001 | Modules that provide shell tab completion for other commands/scripts |
643             | Acme-CPANModules-WorkingWithURL | PERLANCAR | 0.001 | 0.001 | Working with URL |
644             +----------------------------------+-----------+---------------+----------------+----------------------------------------------------------------------+
645              
646             To pick another backend:
647              
648             print generate_table(
649             rows => $rows,
650             backend => "Text::Table::Org",
651             );
652              
653             The result is something like:
654              
655             | Distribution | Author | First Version | Latest Version | Abstract |
656             |----------------------------------+-----------+---------------+----------------+----------------------------------------------------------------------|
657             | ACME-Dzil-Test-daemon | DAEMON | 0.001 | 0.001 | Module abstract placeholder text |
658             | ACME-Dzil-Test-daemon2 | DAEMON | 0.001 | 0.001 | Module abstract placeholder text |
659             | Acme-CPANModules-ShellCompleters | PERLANCAR | 0.001 | 0.001 | Modules that provide shell tab completion for other commands/scripts |
660             | Acme-CPANModules-WorkingWithURL | PERLANCAR | 0.001 | 0.001 | Working with URL |
661              
662             To specify some other options:
663              
664             print generate_table(
665             rows => $rows,
666             header_row => 0, # default is true
667             separate_row => 1, # default is false
668             caption => "Some of the new distributions released in Jan 2022",
669             backend => "Text::Table::Org",
670             );
671              
672             The result is something like:
673              
674             #+CAPTION: Some of the new distributions released in Jan 2022
675             | Distribution | Author | First Version | Latest Version | Abstract |
676             |----------------------------------+-----------+---------------+----------------+----------------------------------------------------------------------|
677             | ACME-Dzil-Test-daemon | DAEMON | 0.001 | 0.001 | Module abstract placeholder text |
678             |----------------------------------+-----------+---------------+----------------+----------------------------------------------------------------------|
679             | ACME-Dzil-Test-daemon2 | DAEMON | 0.001 | 0.001 | Module abstract placeholder text |
680             |----------------------------------+-----------+---------------+----------------+----------------------------------------------------------------------|
681             | Acme-CPANModules-ShellCompleters | PERLANCAR | 0.001 | 0.001 | Modules that provide shell tab completion for other commands/scripts |
682             |----------------------------------+-----------+---------------+----------------+----------------------------------------------------------------------|
683             | Acme-CPANModules-WorkingWithURL | PERLANCAR | 0.001 | 0.001 | Working with URL |
684              
685             To pass backend-specific options:
686              
687             print generate_table(
688             rows => $rows,
689             backend => "Text::Table::More",
690             backend_opts => {
691             border_style => 'ASCII::SingleLineDoubleAfterHeader',
692             align => 'right',
693             row_attrs => [
694             [0, {align=>'middle'}],
695             ],
696             },
697             );
698              
699             The result is something like:
700              
701             .----------------------------------+-----------+---------------+----------------+----------------------------------------------------------------------.
702             | Distribution | Author | First Version | Latest Version | Abstract |
703             +==================================+===========+===============+================+======================================================================+
704             | ACME-Dzil-Test-daemon | DAEMON | 0.001 | 0.001 | Module abstract placeholder text |
705             | ACME-Dzil-Test-daemon2 | DAEMON | 0.001 | 0.001 | Module abstract placeholder text |
706             | Acme-CPANModules-ShellCompleters | PERLANCAR | 0.001 | 0.001 | Modules that provide shell tab completion for other commands/scripts |
707             | Acme-CPANModules-WorkingWithURL | PERLANCAR | 0.001 | 0.001 | Working with URL |
708             `----------------------------------+-----------+---------------+----------------+----------------------------------------------------------------------'
709              
710             =head1 DESCRIPTION
711              
712             This module provides a single function, C<generate_table>, which formats a
713             two-dimensional array of data as text table, using one of several available
714             backends. The interface is modelled after L<Text::Table::Tiny>, but
715             L<Text::Table::Sprintf> is the default backend and although Text::Table::Tiny is
716             among the supported backends, it is not required by this module.
717              
718             =head1 DIFFERENCES WITH TEXT::TABLE::TINY
719              
720             =over
721              
722             =item * 'top_and_tail' option from Text::Table::Tiny is not supported
723              
724             Probably won't be supported. You can pass this option to Text::Table::Tiny
725             backend via L</backend_opts> option.
726              
727             =item * 'style' option from Text::Table::Tiny is not supported
728              
729             Won't be supported because this is specific to Text::Table::Tiny. If you want
730             custom border styles, here are some alternative backends you can use:
731             L<Text::ANSITable>, L<Text::Table::TinyBorderStyle>, L<Text::Table::More>,
732             L<Text::UnicodeBox::Table>.
733              
734             =item * 'indent' option from Text::Table::Tiny is not supported
735              
736             Probably won't be supported. You can indent a multiline string in Perl using
737             something like:
738              
739             $rendered_table =~ s/^/ /mg; # indent each line with two spaces
740              
741             =item * 'compact' option from Text::Table::Tiny is not supported
742              
743             May be supported in the future.
744              
745             =back
746              
747             =head1 VARIABLES
748              
749             =head2 @BACKENDS
750              
751             List of supported backends.
752              
753             =head2 %BACKEND_FEATURES
754              
755             List of features supported by each backend. Hash key is backend name, e.g.
756             C<Text::Table::Sprintf>. Hash value is a hashref containing feature name as
757             hashref key and a boolean value or other value as hashref value to describe the
758             support of that feature by that backend.
759              
760             =head1 FUNCTIONS
761              
762             =head2 table
763              
764             An old name for L</generate_table> function (C<generate_table()> was not
765             available in Text::Table::Tiny < 0.04). This name is not available for export.
766              
767             =head2 generate_table
768              
769             Exportable.
770              
771             Usage:
772              
773             table(%params) => str
774              
775             Except for the C<backend> parameter, the parameters will mostly be passed to the
776             backend, sometimes slightly modified if necessary to achieve the desired effect.
777             If a parameter is not supported by a backend, then it will not be passed to the
778             backend.
779              
780             Known parameters:
781              
782             =over
783              
784             =item * backend
785              
786             Optional. Str, default C<Text::Table::Sprintf>. Pick a backend module. Supported
787             backends:
788              
789             =over
790              
791             =item * L<Term::Table>
792              
793             =item * L<Term::TablePrint>
794              
795             =item * L<Text::ANSITable>
796              
797             =item * L<Text::ASCIITable>
798              
799             =item * L<Text::FormatTable>
800              
801             =item * L<Text::MarkdownTable>
802              
803             =item * L<Text::Table>
804              
805             =item * L<Text::Table::ASV>
806              
807             =item * L<Text::Table::CSV>
808              
809             =item * L<Text::Table::HTML>
810              
811             =item * L<Text::Table::HTML::DataTables>
812              
813             =item * L<Text::Table::LTSV>
814              
815             =item * L<Text::Table::Manifold>
816              
817             =item * L<Text::Table::More>
818              
819             =item * L<Text::Table::Org>
820              
821             =item * L<Text::Table::Paragraph>
822              
823             =item * L<Text::Table::Sprintf>
824              
825             =item * L<Text::Table::TSV>
826              
827             =item * L<Text::Table::TickitWidget>
828              
829             =item * L<Text::Table::Tiny>
830              
831             =item * L<Text::Table::TinyBorderStyle>
832              
833             =item * L<Text::Table::TinyColor>
834              
835             =item * L<Text::Table::TinyColorWide>
836              
837             =item * L<Text::Table::TinyWide>
838              
839             =item * L<Text::Table::XLSX>
840              
841             =item * L<Text::TabularDisplay>
842              
843             =item * L<Text::UnicodeBox::Table>
844              
845             =back
846              
847             Support matrix for each backend:
848              
849             +-------------------------------+-------+--------------------------------------------------------------+--------------+------------------------------------------------------------------------------------------------+---------+------------+------+---------------+
850             | backend | align | align_note | backend_opts | backend_opts_note | caption | header_row | rows | separate_rows |
851             +-------------------------------+-------+--------------------------------------------------------------+--------------+------------------------------------------------------------------------------------------------+---------+------------+------+---------------+
852             | Term::Table | 0 | | | | 0 | 1 | 1 | 0 |
853             | Term::TablePrint | 0 | | | | 0 | 1 | 1 | 0 |
854             | Text::ANSITable | 1 | | | | 0 | 1 | 1 | 1 |
855             | Text::ASCIITable | 1 | | | | 0 | 1 | 1 | 0 |
856             | Text::FormatTable | 1 | c(enter) alignment is not supported, will fallback to l(eft) | | | 0 | 0 | 1 | 0 |
857             | Text::MarkdownTable | 0 | | | | 0 | 1 | 1 | 0 |
858             | Text::Table | 1 | | | | 0 | 0 | 1 | 0 |
859             | Text::Table::ASV | 0 | | 1 | Backend-specific options (backend_opts) will be passed to table() or generate_table() directly | 0 | 1 | 1 | 0 |
860             | Text::Table::CSV | 0 | | 1 | Backend-specific options (backend_opts) will be passed to table() or generate_table() directly | 0 | 1 | 1 | 0 |
861             | Text::Table::HTML | 0 | TODO, backend does not support yet, parameter already passed | 1 | Backend-specific options (backend_opts) will be passed to table() or generate_table() directly | 1 | 1 | 1 | 0 |
862             | Text::Table::HTML::DataTables | 0 | TODO, backend does not support yet, parameter already passed | 1 | Backend-specific options (backend_opts) will be passed to table() or generate_table() directly | 1 | 1 | 1 | 0 |
863             | Text::Table::LTSV | 0 | | 1 | Backend-specific options (backend_opts) will be passed to table() or generate_table() directly | 0 | 0 | 1 | 0 |
864             | Text::Table::Manifold | 1 | | | | 0 | 1 | 1 | 0 |
865             | Text::Table::More | 1 | | 1 | Backend-specific options (backend_opts) will be passed to table() or generate_table() directly | 0 | 1 | 1 | 1 |
866             | Text::Table::Org | 0 | | 1 | Backend-specific options (backend_opts) will be passed to table() or generate_table() directly | 0 | 1 | 1 | 1 |
867             | Text::Table::Paragraph | 0 | | 1 | Backend-specific options (backend_opts) will be passed to table() or generate_table() directly | 0 | 1 | 1 | 0 |
868             | Text::Table::Sprintf | 0 | | 1 | Backend-specific options (backend_opts) will be passed to table() or generate_table() directly | 0 | 1 | 1 | 1 |
869             | Text::Table::TSV | 0 | | 1 | Backend-specific options (backend_opts) will be passed to table() or generate_table() directly | 0 | 0 | 1 | 0 |
870             | Text::Table::TickitWidget | 0 | | 1 | Backend-specific options (backend_opts) will be passed to table() or generate_table() directly | 0 | 1 | 1 | 0 |
871             | Text::Table::Tiny | 1 | | 1 | Backend-specific options (backend_opts) will be passed to table() or generate_table() directly | 0 | 1 | 1 | 1 |
872             | Text::Table::TinyBorderStyle | 0 | TODO, backend does not support yet, parameter already passed | 1 | Backend-specific options (backend_opts) will be passed to table() or generate_table() directly | 0 | 1 | 1 | 1 |
873             | Text::Table::TinyColor | 0 | TODO, backend does not support yet, parameter already passed | 1 | Backend-specific options (backend_opts) will be passed to table() or generate_table() directly | 0 | 1 | 1 | 1 |
874             | Text::Table::TinyColorWide | 0 | TODO, backend does not support yet, parameter already passed | 1 | Backend-specific options (backend_opts) will be passed to table() or generate_table() directly | 0 | 1 | 1 | 1 |
875             | Text::Table::TinyWide | 0 | TODO, backend does not support yet, parameter already passed | 1 | Backend-specific options (backend_opts) will be passed to table() or generate_table() directly | 0 | 1 | 1 | 1 |
876             | Text::Table::XLSX | 0 | | 1 | Backend-specific options (backend_opts) will be passed to table() or generate_table() directly | 0 | 1 | 1 | 0 |
877             | Text::TabularDisplay | 0 | | | | 0 | 0 | 1 | 0 |
878             | Text::UnicodeBox::Table | 0 | TODO: backend supports left/right | | | 0 | 1 | 1 | 0 |
879             +-------------------------------+-------+--------------------------------------------------------------+--------------+------------------------------------------------------------------------------------------------+---------+------------+------+---------------+
880              
881             =item * rows
882              
883             Required. Aoaos (array of array-of-scalars). Each element in the array is a row
884             of data, where each row is an array reference.
885              
886             =item * header_row
887              
888             Optional. Bool, default is true. If given a true value, the first row in the
889             data will be interpreted as a header row, and separated visually from the rest
890             of the table (e.g. with a ruled line). But some backends won't display
891             differently.
892              
893             =item * separate_rows
894              
895             Boolean. Optional. Default is false. If set to true, will draw a separator line
896             after each data row.
897              
898             Not all backends support this.
899              
900             =item * caption
901              
902             Optional. Str. Caption of the table.
903              
904             =item * align
905              
906             Optional. Array of Str or Str.
907              
908             This takes an array ref with one entry per column, to specify the alignment of
909             that column. Legal values are 'l', 'c', and 'r'. You can also specify a single
910             alignment for all columns. ANSI escape codes are handled.
911              
912             Note that some backends like L<Text::ANSITable> and L<Text::Table::More> support
913             per-row or per-cell or even conditional alignment. Some backends like
914             L<Text::ASCIITable> and L<Text::Table> can also align beyond just l(eft),
915             c(enter), r(right), e.g. C<justify> or align on a decimal point. To do more
916             fine-grained alignment setting, you can use the C<backend_opts> parameter.
917              
918             =item * backend_opts
919              
920             Optional. Hashref. Pass backend-specific options to the backend module. Not all
921             backend modules support this, but all backend modules that have interface
922             following C<Text::Table::Tiny> should support this. Also note that as the list
923             of common options is expanded, a previously backend-specific option might be
924             available later as a common option.
925              
926             =back
927              
928             =head2 backends
929              
930             Return list of supported backends. You can also get the list from the
931             L</@BACKENDS> package variable.
932              
933             =head1 HOMEPAGE
934              
935             Please visit the project's homepage at L<https://metacpan.org/release/Text-Table-Any>.
936              
937             =head1 SOURCE
938              
939             Source repository is at L<https://github.com/perlancar/perl-Text-Table-Any>.
940              
941             =head1 SEE ALSO
942              
943             L<Acme::CPANModules::TextTable>
944              
945             =head1 AUTHOR
946              
947             perlancar <perlancar@cpan.org>
948              
949             =head1 CONTRIBUTING
950              
951              
952             To contribute, you can send patches by email/via RT, or send pull requests on
953             GitHub.
954              
955             Most of the time, you don't need to build the distribution yourself. You can
956             simply modify the code, then test via:
957              
958             % prove -l
959              
960             If you want to build the distribution (e.g. to try to install it locally on your
961             system), you can install L<Dist::Zilla>,
962             L<Dist::Zilla::PluginBundle::Author::PERLANCAR>, and sometimes one or two other
963             Dist::Zilla plugin and/or Pod::Weaver::Plugin. Any additional steps required
964             beyond that are considered a bug and can be reported to me.
965              
966             =head1 COPYRIGHT AND LICENSE
967              
968             This software is copyright (c) 2022, 2021, 2020, 2019, 2018, 2017, 2016, 2015 by perlancar <perlancar@cpan.org>.
969              
970             This is free software; you can redistribute it and/or modify it under
971             the same terms as the Perl 5 programming language system itself.
972              
973             =head1 BUGS
974              
975             Please report any bugs or feature requests on the bugtracker website L<https://rt.cpan.org/Public/Dist/Display.html?Name=Text-Table-Any>
976              
977             When submitting a bug or request, please include a test-file or a
978             patch to an existing test-file that illustrates the bug or desired
979             feature.
980              
981             =cut