| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package Data::Format::Pretty::Console; |
|
2
|
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
our $DATE = '2017-07-10'; # DATE |
|
4
|
|
|
|
|
|
|
our $VERSION = '0.38'; # VERSION |
|
5
|
|
|
|
|
|
|
|
|
6
|
1
|
|
|
1
|
|
117919
|
use 5.010001; |
|
|
1
|
|
|
|
|
5
|
|
|
7
|
1
|
|
|
1
|
|
9
|
use strict; |
|
|
1
|
|
|
|
|
3
|
|
|
|
1
|
|
|
|
|
33
|
|
|
8
|
1
|
|
|
1
|
|
9
|
use warnings; |
|
|
1
|
|
|
|
|
3
|
|
|
|
1
|
|
|
|
|
41
|
|
|
9
|
1
|
|
|
1
|
|
8
|
use experimental 'smartmatch'; |
|
|
1
|
|
|
|
|
4
|
|
|
|
1
|
|
|
|
|
8
|
|
|
10
|
1
|
|
|
1
|
|
5167
|
use Log::ger; |
|
|
1
|
|
|
|
|
128
|
|
|
|
1
|
|
|
|
|
7
|
|
|
11
|
|
|
|
|
|
|
|
|
12
|
1
|
|
|
1
|
|
1951
|
use Scalar::Util qw(blessed); |
|
|
1
|
|
|
|
|
3
|
|
|
|
1
|
|
|
|
|
74
|
|
|
13
|
1
|
|
|
1
|
|
859
|
use Text::ANSITable; |
|
|
1
|
|
|
|
|
100571
|
|
|
|
1
|
|
|
|
|
36
|
|
|
14
|
1
|
|
|
1
|
|
381
|
use YAML::Any; |
|
|
1
|
|
|
|
|
904
|
|
|
|
1
|
|
|
|
|
5
|
|
|
15
|
1
|
|
|
1
|
|
3599
|
use JSON::MaybeXS; |
|
|
1
|
|
|
|
|
5513
|
|
|
|
1
|
|
|
|
|
2517
|
|
|
16
|
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
my $json = JSON::MaybeXS->new->allow_nonref; |
|
18
|
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
require Exporter; |
|
20
|
|
|
|
|
|
|
our @ISA = qw(Exporter); |
|
21
|
|
|
|
|
|
|
our @EXPORT_OK = qw(format_pretty); |
|
22
|
|
|
|
|
|
|
|
|
23
|
0
|
|
|
0
|
0
|
0
|
sub content_type { "text/plain" } |
|
24
|
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
sub format_pretty { |
|
26
|
23
|
|
|
23
|
1
|
40849
|
my ($data, $opts) = @_; |
|
27
|
23
|
|
50
|
|
|
139
|
$opts //= {}; |
|
28
|
23
|
|
|
|
|
118
|
__PACKAGE__->new($opts)->_format($data); |
|
29
|
|
|
|
|
|
|
} |
|
30
|
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
# OO interface is nto documented, we use it just to subclass |
|
32
|
|
|
|
|
|
|
# Data::Format::Pretty::HTML |
|
33
|
|
|
|
|
|
|
sub new { |
|
34
|
44
|
|
|
44
|
0
|
115504
|
my ($class, $opts) = @_; |
|
35
|
44
|
|
100
|
|
|
253
|
$opts //= {}; |
|
36
|
44
|
|
33
|
|
|
412
|
$opts->{interactive} //= $ENV{INTERACTIVE} // (-t STDOUT); |
|
|
|
|
66
|
|
|
|
|
|
37
|
|
|
|
|
|
|
$opts->{table_column_orders} //= $json->decode( |
|
38
|
|
|
|
|
|
|
$ENV{FORMAT_PRETTY_TABLE_COLUMN_ORDERS}) |
|
39
|
44
|
50
|
0
|
|
|
179
|
if defined($ENV{FORMAT_PRETTY_TABLE_COLUMN_ORDERS}); |
|
40
|
|
|
|
|
|
|
$opts->{table_column_formats} //= $json->decode( |
|
41
|
|
|
|
|
|
|
$ENV{FORMAT_PRETTY_TABLE_COLUMN_FORMATS}) |
|
42
|
44
|
50
|
0
|
|
|
159
|
if defined($ENV{FORMAT_PRETTY_TABLE_COLUMN_FORMATS}); |
|
43
|
|
|
|
|
|
|
$opts->{table_column_types} //= $json->decode( |
|
44
|
|
|
|
|
|
|
$ENV{FORMAT_PRETTY_TABLE_COLUMN_TYPES}) |
|
45
|
44
|
50
|
0
|
|
|
189
|
if defined($ENV{FORMAT_PRETTY_TABLE_COLUMN_TYPES}); |
|
46
|
44
|
|
33
|
|
|
321
|
$opts->{list_max_columns} //= $ENV{FORMAT_PRETTY_LIST_MAX_COLUMNS}; |
|
47
|
44
|
|
|
|
|
270
|
bless {opts=>$opts}, $class; |
|
48
|
|
|
|
|
|
|
} |
|
49
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
sub _is_cell_or_format_cell { |
|
51
|
147
|
|
|
147
|
|
415
|
my ($self, $data, $is_format) = @_; |
|
52
|
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
# XXX currently hardcoded limits |
|
54
|
147
|
|
|
|
|
329
|
my $maxlen = 1000; |
|
55
|
|
|
|
|
|
|
|
|
56
|
147
|
100
|
66
|
|
|
633
|
if (!ref($data) || blessed($data)) { |
|
|
|
100
|
|
|
|
|
|
|
57
|
135
|
100
|
|
|
|
412
|
if (!defined($data)) { |
|
58
|
8
|
50
|
|
|
|
70
|
return "" if $is_format; |
|
59
|
0
|
|
|
|
|
0
|
return 1; |
|
60
|
|
|
|
|
|
|
} |
|
61
|
127
|
50
|
|
|
|
398
|
if (length($data) > $maxlen) { |
|
62
|
0
|
|
|
|
|
0
|
return; |
|
63
|
|
|
|
|
|
|
} |
|
64
|
127
|
100
|
|
|
|
505
|
return "$data" if $is_format; |
|
65
|
85
|
|
|
|
|
360
|
return 1; |
|
66
|
|
|
|
|
|
|
} elsif (ref($data) eq 'ARRAY') { |
|
67
|
9
|
50
|
|
|
|
28
|
if (grep {ref($_) && !blessed($_)} @$data) { |
|
|
21
|
50
|
|
|
|
94
|
|
|
68
|
0
|
|
|
|
|
0
|
return; |
|
69
|
|
|
|
|
|
|
} |
|
70
|
9
|
50
|
|
|
|
26
|
my $s = join(", ", map {defined($_) ? "$_":""} @$data); |
|
|
21
|
|
|
|
|
87
|
|
|
71
|
9
|
50
|
|
|
|
35
|
if (length($s) > $maxlen) { |
|
72
|
0
|
|
|
|
|
0
|
return; |
|
73
|
|
|
|
|
|
|
} |
|
74
|
9
|
100
|
|
|
|
39
|
return $s if $is_format; |
|
75
|
6
|
|
|
|
|
29
|
return 1; |
|
76
|
|
|
|
|
|
|
} else { |
|
77
|
3
|
|
|
|
|
16
|
return; |
|
78
|
|
|
|
|
|
|
} |
|
79
|
|
|
|
|
|
|
} |
|
80
|
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
# return a string when data can be represented as a cell, otherwise undef. what |
|
82
|
|
|
|
|
|
|
# can be put in a table cell? a string (or stringified object) or array of |
|
83
|
|
|
|
|
|
|
# strings (stringified objects) that is quite "short". |
|
84
|
53
|
|
|
53
|
|
165
|
sub _format_cell { _is_cell_or_format_cell(@_, 1) } |
|
85
|
|
|
|
|
|
|
|
|
86
|
94
|
|
|
94
|
|
289
|
sub _is_cell { _is_cell_or_format_cell(@_, 0) } |
|
87
|
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
sub _detect_struct { |
|
89
|
72
|
|
|
72
|
|
300
|
my ($self, $data) = @_; |
|
90
|
72
|
|
|
|
|
162
|
my $struct; |
|
91
|
72
|
|
|
|
|
178
|
my $struct_meta = {}; |
|
92
|
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
# XXX perhaps, use Data::Schema later? |
|
94
|
|
|
|
|
|
|
CHECK_FORMAT: |
|
95
|
|
|
|
|
|
|
{ |
|
96
|
72
|
|
|
|
|
173
|
CHECK_SCALAR: |
|
97
|
|
|
|
|
|
|
{ |
|
98
|
72
|
100
|
100
|
|
|
148
|
if (!ref($data) || blessed($data)) { |
|
|
72
|
|
|
|
|
485
|
|
|
99
|
27
|
|
|
|
|
70
|
$struct = "scalar"; |
|
100
|
27
|
|
|
|
|
78
|
last CHECK_FORMAT; |
|
101
|
|
|
|
|
|
|
} |
|
102
|
|
|
|
|
|
|
} |
|
103
|
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
CHECK_AOA: |
|
105
|
|
|
|
|
|
|
{ |
|
106
|
45
|
100
|
|
|
|
123
|
if (ref($data) eq 'ARRAY') { |
|
|
45
|
|
|
|
|
185
|
|
|
107
|
24
|
|
|
|
|
62
|
my $numcols; |
|
108
|
24
|
|
|
|
|
79
|
for my $row (@$data) { |
|
109
|
27
|
100
|
|
|
|
129
|
last CHECK_AOA unless ref($row) eq 'ARRAY'; |
|
110
|
10
|
100
|
100
|
|
|
58
|
last CHECK_AOA if defined($numcols) && $numcols != @$row; |
|
111
|
8
|
50
|
|
|
|
25
|
last CHECK_AOA if grep { !$self->_is_cell($_) } @$row; |
|
|
18
|
|
|
|
|
56
|
|
|
112
|
8
|
|
|
|
|
27
|
$numcols = @$row; |
|
113
|
|
|
|
|
|
|
} |
|
114
|
5
|
|
|
|
|
15
|
$struct = "aoa"; |
|
115
|
5
|
|
|
|
|
16
|
last CHECK_FORMAT; |
|
116
|
|
|
|
|
|
|
} |
|
117
|
|
|
|
|
|
|
} |
|
118
|
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
CHECK_AOH: |
|
120
|
|
|
|
|
|
|
{ |
|
121
|
40
|
100
|
|
|
|
96
|
if (ref($data) eq 'ARRAY') { |
|
|
40
|
|
|
|
|
149
|
|
|
122
|
19
|
|
|
|
|
74
|
$struct_meta->{columns} = {}; |
|
123
|
19
|
|
|
|
|
62
|
for my $row (@$data) { |
|
124
|
27
|
100
|
|
|
|
101
|
last CHECK_AOH unless ref($row) eq 'HASH'; |
|
125
|
20
|
|
|
|
|
94
|
for my $k (keys %$row) { |
|
126
|
47
|
50
|
|
|
|
168
|
last CHECK_AOH if !$self->_is_cell($row->{$k}); |
|
127
|
47
|
|
|
|
|
179
|
$struct_meta->{columns}{$k} = 1; |
|
128
|
|
|
|
|
|
|
} |
|
129
|
|
|
|
|
|
|
} |
|
130
|
12
|
|
|
|
|
38
|
$struct = "aoh"; |
|
131
|
12
|
|
|
|
|
37
|
last CHECK_FORMAT; |
|
132
|
|
|
|
|
|
|
} |
|
133
|
|
|
|
|
|
|
} |
|
134
|
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
# list of scalars/cells |
|
136
|
|
|
|
|
|
|
CHECK_LIST: |
|
137
|
|
|
|
|
|
|
{ |
|
138
|
28
|
100
|
|
|
|
62
|
if (ref($data) eq 'ARRAY') { |
|
|
28
|
|
|
|
|
97
|
|
|
139
|
7
|
|
|
|
|
23
|
for (@$data) { |
|
140
|
16
|
50
|
|
|
|
53
|
last CHECK_LIST unless $self->_is_cell($_); |
|
141
|
|
|
|
|
|
|
} |
|
142
|
7
|
|
|
|
|
18
|
$struct = "list"; |
|
143
|
7
|
|
|
|
|
20
|
last CHECK_FORMAT; |
|
144
|
|
|
|
|
|
|
} |
|
145
|
|
|
|
|
|
|
} |
|
146
|
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
# hash which contains at least one "table" (list/aoa/aoh) |
|
148
|
|
|
|
|
|
|
CHECK_HOT: |
|
149
|
|
|
|
|
|
|
{ |
|
150
|
21
|
50
|
|
|
|
47
|
last CHECK_HOT if $self->{opts}{skip_hot}; |
|
|
21
|
|
|
|
|
78
|
|
|
151
|
21
|
50
|
|
|
|
73
|
last CHECK_HOT unless ref($data) eq 'HASH'; |
|
152
|
21
|
|
|
|
|
47
|
my $has_t; |
|
153
|
21
|
|
|
|
|
105
|
while (my ($k, $v) = each %$data) { |
|
154
|
24
|
|
|
|
|
124
|
my ($s2, $sm2) = $self->_detect_struct($v, {skip_hot=>1}); |
|
155
|
24
|
50
|
|
|
|
97
|
last CHECK_HOT unless $s2; |
|
156
|
24
|
100
|
|
|
|
200
|
$has_t = 1 if $s2 =~ /^(?:list|aoa|aoh|hash)$/; |
|
157
|
|
|
|
|
|
|
} |
|
158
|
21
|
100
|
|
|
|
74
|
last CHECK_HOT unless $has_t; |
|
159
|
7
|
|
|
|
|
19
|
$struct = "hot"; |
|
160
|
7
|
|
|
|
|
16
|
last CHECK_FORMAT; |
|
161
|
|
|
|
|
|
|
} |
|
162
|
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
# hash of scalars/cells |
|
164
|
|
|
|
|
|
|
CHECK_HASH: |
|
165
|
|
|
|
|
|
|
{ |
|
166
|
14
|
50
|
|
|
|
33
|
if (ref($data) eq 'HASH') { |
|
|
14
|
|
|
|
|
55
|
|
|
167
|
14
|
|
|
|
|
46
|
for (values %$data) { |
|
168
|
13
|
100
|
|
|
|
49
|
last CHECK_HASH unless $self->_is_cell($_); |
|
169
|
|
|
|
|
|
|
} |
|
170
|
11
|
|
|
|
|
29
|
$struct = "hash"; |
|
171
|
11
|
|
|
|
|
33
|
last CHECK_FORMAT; |
|
172
|
|
|
|
|
|
|
} |
|
173
|
|
|
|
|
|
|
} |
|
174
|
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
} |
|
176
|
|
|
|
|
|
|
|
|
177
|
72
|
|
|
|
|
293
|
($struct, $struct_meta); |
|
178
|
|
|
|
|
|
|
} |
|
179
|
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
# t (table) is a structure like this: {cols=>["colName1", "colName2", ...]}, |
|
181
|
|
|
|
|
|
|
# rows=>[ [row1.1, row1.2, ...], [row2.1, row2.2, ...], ... ], at_opts=>{...}, |
|
182
|
|
|
|
|
|
|
# col_widths=>{colName1=>5, ...}}. the job of this routine is to render it |
|
183
|
|
|
|
|
|
|
# (currently uses Text::ANSITable). |
|
184
|
|
|
|
|
|
|
sub _render_table { |
|
185
|
12
|
|
|
12
|
|
47
|
my ($self, $t) = @_; |
|
186
|
|
|
|
|
|
|
|
|
187
|
12
|
|
|
|
|
35
|
my $colfmts; |
|
188
|
12
|
|
|
|
|
41
|
my $tcff = $self->{opts}{table_column_formats}; |
|
189
|
12
|
100
|
|
|
|
52
|
if ($tcff) { |
|
190
|
2
|
|
|
|
|
10
|
for my $tcf (@$tcff) { |
|
191
|
2
|
|
|
|
|
7
|
my $match = 1; |
|
192
|
2
|
|
|
|
|
7
|
my @tcols = @{ $t->{cols} }; |
|
|
2
|
|
|
|
|
11
|
|
|
193
|
2
|
|
|
|
|
14
|
for my $scol (keys %$tcf) { |
|
194
|
3
|
50
|
|
|
|
19
|
do { $match = 0; last } unless $scol ~~ @tcols; |
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
195
|
|
|
|
|
|
|
} |
|
196
|
2
|
50
|
|
|
|
11
|
if ($match) { |
|
197
|
2
|
|
|
|
|
4
|
$colfmts = $tcf; |
|
198
|
2
|
|
|
|
|
8
|
last; |
|
199
|
|
|
|
|
|
|
} |
|
200
|
|
|
|
|
|
|
} |
|
201
|
|
|
|
|
|
|
} |
|
202
|
|
|
|
|
|
|
|
|
203
|
12
|
|
|
|
|
30
|
my $coltypes; |
|
204
|
12
|
|
|
|
|
35
|
my $tctt = $self->{opts}{table_column_types}; |
|
205
|
12
|
50
|
|
|
|
46
|
if ($tctt) { |
|
206
|
0
|
|
|
|
|
0
|
for my $tct (@$tctt) { |
|
207
|
0
|
|
|
|
|
0
|
my $match = 1; |
|
208
|
0
|
|
|
|
|
0
|
my @tcols = @{ $t->{cols} }; |
|
|
0
|
|
|
|
|
0
|
|
|
209
|
0
|
|
|
|
|
0
|
for my $scol (keys %$tct) { |
|
210
|
0
|
0
|
|
|
|
0
|
do { $match = 0; last } unless $scol ~~ @tcols; |
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
211
|
|
|
|
|
|
|
} |
|
212
|
0
|
0
|
|
|
|
0
|
if ($match) { |
|
213
|
0
|
|
|
|
|
0
|
$coltypes = $tct; |
|
214
|
0
|
|
|
|
|
0
|
last; |
|
215
|
|
|
|
|
|
|
} |
|
216
|
|
|
|
|
|
|
} |
|
217
|
|
|
|
|
|
|
} |
|
218
|
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
# render using Text::ANSITable |
|
220
|
12
|
|
|
|
|
408
|
my $at = Text::ANSITable->new; |
|
221
|
12
|
|
|
|
|
126824
|
$at->columns($t->{cols}); |
|
222
|
12
|
|
|
|
|
525
|
$at->rows($t->{rows}); |
|
223
|
12
|
100
|
|
|
|
360
|
if ($t->{at_opts}) { |
|
224
|
7
|
|
|
|
|
22
|
$at->{$_} = $t->{at_opts}{$_} for keys %{ $t->{at_opts} }; |
|
|
7
|
|
|
|
|
42
|
|
|
225
|
|
|
|
|
|
|
} |
|
226
|
12
|
100
|
|
|
|
58
|
if ($colfmts) { |
|
227
|
|
|
|
|
|
|
$at->set_column_style($_ => formats => $colfmts->{$_}) |
|
228
|
2
|
|
|
|
|
26
|
for keys %$colfmts; |
|
229
|
|
|
|
|
|
|
} |
|
230
|
12
|
50
|
|
|
|
212
|
if ($coltypes) { |
|
231
|
|
|
|
|
|
|
$at->set_column_style($_ => type => $coltypes->{$_}) |
|
232
|
0
|
|
|
|
|
0
|
for keys %$coltypes; |
|
233
|
|
|
|
|
|
|
} |
|
234
|
12
|
50
|
|
|
|
57
|
if ($t->{col_widths}) { |
|
235
|
|
|
|
|
|
|
$at->set_column_style($_ => width => $t->{col_widths}{$_}) |
|
236
|
0
|
|
|
|
|
0
|
for keys %{ $t->{col_widths} }; |
|
|
0
|
|
|
|
|
0
|
|
|
237
|
|
|
|
|
|
|
} |
|
238
|
12
|
|
|
|
|
76
|
$at->draw; |
|
239
|
|
|
|
|
|
|
} |
|
240
|
|
|
|
|
|
|
|
|
241
|
|
|
|
|
|
|
# format unknown structure, the default is to dump YAML structure |
|
242
|
|
|
|
|
|
|
sub _format_unknown { |
|
243
|
2
|
|
|
2
|
|
6
|
my ($self, $data) = @_; |
|
244
|
2
|
|
|
|
|
15
|
Dump($data); |
|
245
|
|
|
|
|
|
|
} |
|
246
|
|
|
|
|
|
|
|
|
247
|
|
|
|
|
|
|
sub _format_scalar { |
|
248
|
8
|
|
|
8
|
|
36
|
my ($self, $data) = @_; |
|
249
|
|
|
|
|
|
|
|
|
250
|
8
|
100
|
|
|
|
29
|
my $sdata = defined($data) ? "$data" : ""; |
|
251
|
8
|
100
|
|
|
|
34
|
return "" if !length($sdata); |
|
252
|
6
|
100
|
|
|
|
51
|
return $sdata =~ /\n\z/s ? $sdata : "$sdata\n"; |
|
253
|
|
|
|
|
|
|
} |
|
254
|
|
|
|
|
|
|
|
|
255
|
|
|
|
|
|
|
sub _format_list { |
|
256
|
3
|
|
|
3
|
|
12
|
my ($self, $data) = @_; |
|
257
|
3
|
50
|
|
|
|
16
|
if ($self->{opts}{interactive}) { |
|
258
|
|
|
|
|
|
|
|
|
259
|
3
|
|
|
|
|
33
|
require List::Util; |
|
260
|
3
|
|
|
|
|
791
|
require POSIX; |
|
261
|
|
|
|
|
|
|
|
|
262
|
|
|
|
|
|
|
# format list as as columns (a la 'ls' output) |
|
263
|
|
|
|
|
|
|
|
|
264
|
3
|
|
|
|
|
7525
|
my @rows = map { $self->_format_cell($_) } @$data; |
|
|
7
|
|
|
|
|
28
|
|
|
265
|
|
|
|
|
|
|
|
|
266
|
3
|
|
50
|
|
|
13
|
my $maxwidth = List::Util::max(map { length } @rows) // 0; |
|
|
7
|
|
|
|
|
33
|
|
|
267
|
3
|
|
|
|
|
10
|
my ($termcols, $termrows); |
|
268
|
3
|
50
|
|
|
|
17
|
if ($ENV{COLUMNS}) { |
|
|
|
50
|
|
|
|
|
|
|
269
|
0
|
|
|
|
|
0
|
$termcols = $ENV{COLUMNS}; |
|
270
|
3
|
|
|
|
|
601
|
} elsif (eval { require Term::Size; 1 }) { |
|
|
3
|
|
|
|
|
801
|
|
|
271
|
3
|
|
|
|
|
60
|
($termcols, $termrows) = Term::Size::chars(); |
|
272
|
|
|
|
|
|
|
} else { |
|
273
|
|
|
|
|
|
|
# sane default, on windows we need to offset by 1 because printing |
|
274
|
|
|
|
|
|
|
# at the rightmost column will cause cursor to move down one line. |
|
275
|
0
|
0
|
|
|
|
0
|
$termcols = $^O =~ /Win/ ? 79 : 80; |
|
276
|
|
|
|
|
|
|
} |
|
277
|
3
|
|
|
|
|
11
|
my $numcols = 1; |
|
278
|
3
|
50
|
|
|
|
14
|
if ($maxwidth) { |
|
279
|
|
|
|
|
|
|
# | some-text-some | some-text-some... | |
|
280
|
|
|
|
|
|
|
# 2/\__maxwidth__/\3/\__maxwidth__/...\2 |
|
281
|
|
|
|
|
|
|
# |
|
282
|
|
|
|
|
|
|
# table width = (2+maxwidth) + (3+maxwidth)*(numcols-1) + 2 |
|
283
|
|
|
|
|
|
|
# |
|
284
|
|
|
|
|
|
|
# so with a bit of algrebra, solve for numcols: |
|
285
|
3
|
|
|
|
|
17
|
$numcols = int( (($termcols-1)-$maxwidth-6)/(3+$maxwidth) + 1 ); |
|
286
|
3
|
50
|
|
|
|
14
|
$numcols = @rows if $numcols > @rows; |
|
287
|
3
|
50
|
|
|
|
16
|
$numcols = 1 if $numcols < 1; |
|
288
|
|
|
|
|
|
|
} |
|
289
|
|
|
|
|
|
|
$numcols = $self->{opts}{list_max_columns} |
|
290
|
|
|
|
|
|
|
if defined($self->{opts}{list_max_columns}) && |
|
291
|
3
|
50
|
33
|
|
|
31
|
$numcols > $self->{opts}{list_max_columns}; |
|
292
|
3
|
|
|
|
|
26
|
my $numrows = POSIX::ceil(@rows/$numcols); |
|
293
|
3
|
50
|
|
|
|
13
|
if ($numrows) { |
|
294
|
|
|
|
|
|
|
# reduce number of columns to avoid empty columns |
|
295
|
3
|
|
|
|
|
15
|
$numcols = POSIX::ceil(@rows/$numrows); |
|
296
|
|
|
|
|
|
|
} |
|
297
|
|
|
|
|
|
|
#say "D: $numcols x $numrows"; |
|
298
|
|
|
|
|
|
|
|
|
299
|
3
|
|
|
|
|
22
|
my $t = {rows=>[], at_opts=>{show_header=>0}}; |
|
300
|
3
|
|
|
|
|
13
|
$t->{cols} = [map { "c$_" } 1..$numcols]; |
|
|
3
|
|
|
|
|
22
|
|
|
301
|
3
|
50
|
|
|
|
15
|
if ($numcols > 1) { |
|
302
|
0
|
|
|
|
|
0
|
$t->{col_widths}{"c$_"} = $maxwidth for 1..$numcols; |
|
303
|
|
|
|
|
|
|
} |
|
304
|
3
|
|
|
|
|
14
|
for my $r (1..$numrows) { |
|
305
|
7
|
|
|
|
|
16
|
my @trow; |
|
306
|
7
|
|
|
|
|
21
|
for my $c (1..$numcols) { |
|
307
|
7
|
|
|
|
|
20
|
my $idx = ($c-1)*$numrows + ($r-1); |
|
308
|
7
|
50
|
|
|
|
37
|
push @trow, $idx < @rows ? $rows[$idx] : ''; |
|
309
|
|
|
|
|
|
|
} |
|
310
|
7
|
|
|
|
|
17
|
push @{$t->{rows}}, \@trow; |
|
|
7
|
|
|
|
|
26
|
|
|
311
|
|
|
|
|
|
|
} |
|
312
|
|
|
|
|
|
|
|
|
313
|
3
|
|
|
|
|
17
|
return $self->_render_table($t); |
|
314
|
|
|
|
|
|
|
|
|
315
|
|
|
|
|
|
|
} else { |
|
316
|
0
|
|
|
|
|
0
|
my @rows; |
|
317
|
0
|
|
|
|
|
0
|
for my $row (@$data) { |
|
318
|
0
|
|
0
|
|
|
0
|
push @rows, ($row // "") . "\n"; |
|
319
|
|
|
|
|
|
|
} |
|
320
|
0
|
|
|
|
|
0
|
return join("", @rows); |
|
321
|
|
|
|
|
|
|
} |
|
322
|
|
|
|
|
|
|
} |
|
323
|
|
|
|
|
|
|
|
|
324
|
|
|
|
|
|
|
sub _format_hash { |
|
325
|
3
|
|
|
3
|
|
13
|
my ($self, $data) = @_; |
|
326
|
|
|
|
|
|
|
# format hash as two-column table |
|
327
|
3
|
50
|
|
|
|
14
|
if ($self->{opts}{interactive}) { |
|
328
|
3
|
|
|
|
|
25
|
my $t = {cols=>[qw/key value/], rows=>[], |
|
329
|
|
|
|
|
|
|
at_opts=>{}}; |
|
330
|
3
|
|
|
|
|
18
|
for my $k (sort keys %$data) { |
|
331
|
4
|
|
|
|
|
10
|
push @{ $t->{rows} }, [$k, $self->_format_cell($data->{$k})]; |
|
|
4
|
|
|
|
|
22
|
|
|
332
|
|
|
|
|
|
|
} |
|
333
|
3
|
|
|
|
|
15
|
return $self->_render_table($t); |
|
334
|
|
|
|
|
|
|
} else { |
|
335
|
0
|
|
|
|
|
0
|
my @t; |
|
336
|
0
|
|
|
|
|
0
|
for my $k (sort keys %$data) { |
|
337
|
0
|
|
0
|
|
|
0
|
push @t, $k, "\t", ($data->{$k} // ""), "\n"; |
|
338
|
|
|
|
|
|
|
} |
|
339
|
0
|
|
|
|
|
0
|
return join("", @t); |
|
340
|
|
|
|
|
|
|
} |
|
341
|
|
|
|
|
|
|
} |
|
342
|
|
|
|
|
|
|
|
|
343
|
|
|
|
|
|
|
sub _format_aoa { |
|
344
|
3
|
|
|
3
|
|
11
|
my ($self, $data) = @_; |
|
345
|
|
|
|
|
|
|
# show aoa as table |
|
346
|
3
|
100
|
|
|
|
22
|
if ($self->{opts}{interactive}) { |
|
347
|
2
|
100
|
|
|
|
8
|
if (@$data) { |
|
348
|
1
|
|
|
|
|
7
|
my $t = {rows=>[], at_opts=>{}}; |
|
349
|
1
|
|
|
|
|
4
|
$t->{cols} = [map { "column$_" } 0..@{ $data->[0] }-1]; |
|
|
2
|
|
|
|
|
11
|
|
|
|
1
|
|
|
|
|
5
|
|
|
350
|
1
|
|
|
|
|
6
|
for my $i (0..@$data-1) { |
|
351
|
2
|
|
|
|
|
7
|
push @{ $t->{rows} }, |
|
352
|
2
|
|
|
|
|
5
|
[map {$self->_format_cell($_)} @{ $data->[$i] }]; |
|
|
4
|
|
|
|
|
14
|
|
|
|
2
|
|
|
|
|
6
|
|
|
353
|
|
|
|
|
|
|
} |
|
354
|
1
|
|
|
|
|
6
|
return $self->_render_table($t); |
|
355
|
|
|
|
|
|
|
} else { |
|
356
|
1
|
|
|
|
|
5
|
return ""; |
|
357
|
|
|
|
|
|
|
} |
|
358
|
|
|
|
|
|
|
} else { |
|
359
|
|
|
|
|
|
|
# tab-separated |
|
360
|
1
|
|
|
|
|
4
|
my @t; |
|
361
|
1
|
|
|
|
|
4
|
for my $row (@$data) { |
|
362
|
2
|
|
|
|
|
7
|
push @t, join("\t", map { $self->_format_cell($_) } @$row) . |
|
|
4
|
|
|
|
|
15
|
|
|
363
|
|
|
|
|
|
|
"\n"; |
|
364
|
|
|
|
|
|
|
} |
|
365
|
1
|
|
|
|
|
8
|
return join("", @t); |
|
366
|
|
|
|
|
|
|
} |
|
367
|
|
|
|
|
|
|
} |
|
368
|
|
|
|
|
|
|
|
|
369
|
|
|
|
|
|
|
sub _format_aoh { |
|
370
|
6
|
|
|
6
|
|
22
|
my ($self, $data, $struct_meta) = @_; |
|
371
|
|
|
|
|
|
|
# show aoh as table |
|
372
|
6
|
|
|
|
|
16
|
my @cols = @{ $self->_order_table_columns( |
|
373
|
6
|
|
|
|
|
17
|
[keys %{$struct_meta->{columns}}]) }; |
|
|
6
|
|
|
|
|
57
|
|
|
374
|
6
|
100
|
|
|
|
36
|
if ($self->{opts}{interactive}) { |
|
375
|
5
|
|
|
|
|
34
|
my $t = {cols=>\@cols, rows=>[]}; |
|
376
|
5
|
|
|
|
|
28
|
for my $i (0..@$data-1) { |
|
377
|
8
|
|
|
|
|
25
|
my $row = $data->[$i]; |
|
378
|
8
|
|
|
|
|
19
|
push @{ $t->{rows} }, [map {$self->_format_cell($row->{$_})} @cols]; |
|
|
8
|
|
|
|
|
31
|
|
|
|
25
|
|
|
|
|
94
|
|
|
379
|
|
|
|
|
|
|
} |
|
380
|
5
|
|
|
|
|
28
|
return $self->_render_table($t); |
|
381
|
|
|
|
|
|
|
} else { |
|
382
|
|
|
|
|
|
|
# tab-separated |
|
383
|
1
|
|
|
|
|
4
|
my @t; |
|
384
|
1
|
|
|
|
|
4
|
for my $row (@$data) { |
|
385
|
3
|
|
|
|
|
9
|
my @row = map {$self->_format_cell($row->{$_})} @cols; |
|
|
9
|
|
|
|
|
33
|
|
|
386
|
3
|
|
|
|
|
19
|
push @t, join("\t", @row) . "\n"; |
|
387
|
|
|
|
|
|
|
} |
|
388
|
1
|
|
|
|
|
10
|
return join("", @t); |
|
389
|
|
|
|
|
|
|
} |
|
390
|
|
|
|
|
|
|
} |
|
391
|
|
|
|
|
|
|
|
|
392
|
|
|
|
|
|
|
sub _format_hot { |
|
393
|
2
|
|
|
2
|
|
8
|
my ($self, $data) = @_; |
|
394
|
|
|
|
|
|
|
# show hot as paragraphs: |
|
395
|
|
|
|
|
|
|
# |
|
396
|
|
|
|
|
|
|
# key: |
|
397
|
|
|
|
|
|
|
# value (table) |
|
398
|
|
|
|
|
|
|
# |
|
399
|
|
|
|
|
|
|
# key2: |
|
400
|
|
|
|
|
|
|
# value ... |
|
401
|
2
|
|
|
|
|
6
|
my @t; |
|
402
|
2
|
|
|
|
|
11
|
for my $k (sort keys %$data) { |
|
403
|
4
|
|
|
|
|
4704
|
push @t, "$k:\n", $self->_format($data->{$k}), "\n"; |
|
404
|
|
|
|
|
|
|
} |
|
405
|
2
|
|
|
|
|
7808
|
return join("", @t); |
|
406
|
|
|
|
|
|
|
} |
|
407
|
|
|
|
|
|
|
|
|
408
|
|
|
|
|
|
|
sub _format { |
|
409
|
27
|
|
|
27
|
|
90
|
my ($self, $data) = @_; |
|
410
|
|
|
|
|
|
|
|
|
411
|
27
|
|
|
|
|
105
|
my ($struct, $struct_meta) = $self->_detect_struct($data); |
|
412
|
|
|
|
|
|
|
|
|
413
|
27
|
100
|
|
|
|
211
|
if (!$struct) { |
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
414
|
2
|
|
|
|
|
9
|
return $self->_format_unknown($data, $struct_meta); |
|
415
|
|
|
|
|
|
|
} elsif ($struct eq 'scalar') { |
|
416
|
8
|
|
|
|
|
30
|
return $self->_format_scalar($data, $struct_meta); |
|
417
|
|
|
|
|
|
|
} elsif ($struct eq 'list') { |
|
418
|
3
|
|
|
|
|
19
|
return $self->_format_list($data, $struct_meta); |
|
419
|
|
|
|
|
|
|
} elsif ($struct eq 'hash') { |
|
420
|
3
|
|
|
|
|
15
|
return $self->_format_hash($data, $struct_meta); |
|
421
|
|
|
|
|
|
|
} elsif ($struct eq 'aoa') { |
|
422
|
3
|
|
|
|
|
13
|
return $self->_format_aoa($data, $struct_meta); |
|
423
|
|
|
|
|
|
|
} elsif ($struct eq 'aoh') { |
|
424
|
6
|
|
|
|
|
38
|
return $self->_format_aoh($data, $struct_meta); |
|
425
|
|
|
|
|
|
|
} elsif ($struct eq 'hot') { |
|
426
|
2
|
|
|
|
|
11
|
return $self->_format_hot($data, $struct_meta); |
|
427
|
|
|
|
|
|
|
} else { |
|
428
|
0
|
|
|
|
|
0
|
die "BUG: Unknown format `$struct`"; |
|
429
|
|
|
|
|
|
|
} |
|
430
|
|
|
|
|
|
|
} |
|
431
|
|
|
|
|
|
|
|
|
432
|
|
|
|
|
|
|
sub _order_table_columns { |
|
433
|
|
|
|
|
|
|
#$log->tracef('=> _order_table_columns(%s)', \@_); |
|
434
|
6
|
|
|
6
|
|
22
|
my ($self, $cols) = @_; |
|
435
|
|
|
|
|
|
|
|
|
436
|
6
|
|
|
|
|
19
|
my $found; # whether we found an ordering in table_column_orders |
|
437
|
6
|
|
|
|
|
22
|
my $tco = $self->{opts}{table_column_orders}; |
|
438
|
6
|
|
|
|
|
21
|
my %orders; # colname => idx |
|
439
|
6
|
100
|
|
|
|
33
|
if ($tco) { |
|
440
|
2
|
50
|
|
|
|
13
|
die "table_column_orders should be an arrayref" |
|
441
|
|
|
|
|
|
|
unless ref($tco) eq 'ARRAY'; |
|
442
|
|
|
|
|
|
|
CO: |
|
443
|
2
|
|
|
|
|
8
|
for my $co (@$tco) { |
|
444
|
2
|
50
|
|
|
|
12
|
die "table_column_orders elements must all be arrayrefs" |
|
445
|
|
|
|
|
|
|
unless ref($co) eq 'ARRAY'; |
|
446
|
2
|
|
|
|
|
10
|
for (@$co) { |
|
447
|
7
|
100
|
|
|
|
39
|
next CO unless $_ ~~ @$cols; |
|
448
|
|
|
|
|
|
|
} |
|
449
|
|
|
|
|
|
|
|
|
450
|
1
|
|
|
|
|
4
|
$found++; |
|
451
|
1
|
|
|
|
|
6
|
for (my $i=0; $i<@$co; $i++) { |
|
452
|
3
|
|
|
|
|
14
|
$orders{$co->[$i]} = $i; |
|
453
|
|
|
|
|
|
|
} |
|
454
|
1
|
|
|
|
|
3
|
$found++; |
|
455
|
1
|
|
|
|
|
4
|
last CO; |
|
456
|
|
|
|
|
|
|
} |
|
457
|
|
|
|
|
|
|
} |
|
458
|
|
|
|
|
|
|
|
|
459
|
6
|
|
|
|
|
17
|
my @ocols; |
|
460
|
6
|
100
|
|
|
|
24
|
if ($found) { |
|
461
|
|
|
|
|
|
|
@ocols = sort { |
|
462
|
1
|
|
|
|
|
8
|
(defined($orders{$a}) && defined($orders{$b}) ? |
|
463
|
10
|
100
|
100
|
|
|
62
|
$orders{$a} <=> $orders{$b} : 0) |
|
|
|
50
|
|
|
|
|
|
|
464
|
|
|
|
|
|
|
|| $a cmp $b |
|
465
|
|
|
|
|
|
|
} (sort @$cols); |
|
466
|
|
|
|
|
|
|
} else { |
|
467
|
5
|
|
|
|
|
33
|
@ocols = sort @$cols; |
|
468
|
|
|
|
|
|
|
} |
|
469
|
|
|
|
|
|
|
|
|
470
|
6
|
|
|
|
|
41
|
\@ocols; |
|
471
|
|
|
|
|
|
|
} |
|
472
|
|
|
|
|
|
|
|
|
473
|
|
|
|
|
|
|
1; |
|
474
|
|
|
|
|
|
|
# ABSTRACT: Pretty-print data structure for console output |
|
475
|
|
|
|
|
|
|
|
|
476
|
|
|
|
|
|
|
__END__ |
|
477
|
|
|
|
|
|
|
|
|
478
|
|
|
|
|
|
|
=pod |
|
479
|
|
|
|
|
|
|
|
|
480
|
|
|
|
|
|
|
=encoding UTF-8 |
|
481
|
|
|
|
|
|
|
|
|
482
|
|
|
|
|
|
|
=head1 NAME |
|
483
|
|
|
|
|
|
|
|
|
484
|
|
|
|
|
|
|
Data::Format::Pretty::Console - Pretty-print data structure for console output |
|
485
|
|
|
|
|
|
|
|
|
486
|
|
|
|
|
|
|
=head1 VERSION |
|
487
|
|
|
|
|
|
|
|
|
488
|
|
|
|
|
|
|
This document describes version 0.38 of Data::Format::Pretty::Console (from Perl distribution Data-Format-Pretty-Console), released on 2017-07-10. |
|
489
|
|
|
|
|
|
|
|
|
490
|
|
|
|
|
|
|
=head1 SYNOPSIS |
|
491
|
|
|
|
|
|
|
|
|
492
|
|
|
|
|
|
|
In your program: |
|
493
|
|
|
|
|
|
|
|
|
494
|
|
|
|
|
|
|
use Data::Format::Pretty::Console qw(format_pretty); |
|
495
|
|
|
|
|
|
|
... |
|
496
|
|
|
|
|
|
|
print format_pretty($result); |
|
497
|
|
|
|
|
|
|
|
|
498
|
|
|
|
|
|
|
Some example output: |
|
499
|
|
|
|
|
|
|
|
|
500
|
|
|
|
|
|
|
Scalar, format_pretty("foo"): |
|
501
|
|
|
|
|
|
|
|
|
502
|
|
|
|
|
|
|
foo |
|
503
|
|
|
|
|
|
|
|
|
504
|
|
|
|
|
|
|
List, format_pretty([1..21]): |
|
505
|
|
|
|
|
|
|
|
|
506
|
|
|
|
|
|
|
.------------------------------------------------------. |
|
507
|
|
|
|
|
|
|
| 1 | 3 | 5 | 7 | 9 | 11 | 13 | 15 | 17 | 19 | 21 | |
|
508
|
|
|
|
|
|
|
| 2 | 4 | 6 | 8 | 10 | 12 | 14 | 16 | 18 | 20 | | |
|
509
|
|
|
|
|
|
|
'----+----+----+----+----+----+----+----+----+----+----' |
|
510
|
|
|
|
|
|
|
|
|
511
|
|
|
|
|
|
|
The same list, when program output is being piped (that is, (-t STDOUT) is |
|
512
|
|
|
|
|
|
|
false): |
|
513
|
|
|
|
|
|
|
|
|
514
|
|
|
|
|
|
|
1 |
|
515
|
|
|
|
|
|
|
2 |
|
516
|
|
|
|
|
|
|
3 |
|
517
|
|
|
|
|
|
|
4 |
|
518
|
|
|
|
|
|
|
5 |
|
519
|
|
|
|
|
|
|
6 |
|
520
|
|
|
|
|
|
|
7 |
|
521
|
|
|
|
|
|
|
8 |
|
522
|
|
|
|
|
|
|
9 |
|
523
|
|
|
|
|
|
|
10 |
|
524
|
|
|
|
|
|
|
11 |
|
525
|
|
|
|
|
|
|
12 |
|
526
|
|
|
|
|
|
|
14 |
|
527
|
|
|
|
|
|
|
15 |
|
528
|
|
|
|
|
|
|
16 |
|
529
|
|
|
|
|
|
|
17 |
|
530
|
|
|
|
|
|
|
18 |
|
531
|
|
|
|
|
|
|
19 |
|
532
|
|
|
|
|
|
|
20 |
|
533
|
|
|
|
|
|
|
21 |
|
534
|
|
|
|
|
|
|
|
|
535
|
|
|
|
|
|
|
Hash, format_pretty({foo=>"data",bar=>"format",baz=>"pretty",qux=>"console"}): |
|
536
|
|
|
|
|
|
|
|
|
537
|
|
|
|
|
|
|
+-----+---------+ |
|
538
|
|
|
|
|
|
|
| bar | format | |
|
539
|
|
|
|
|
|
|
| baz | pretty | |
|
540
|
|
|
|
|
|
|
| foo | data | |
|
541
|
|
|
|
|
|
|
| qux | console | |
|
542
|
|
|
|
|
|
|
'-----+---------' |
|
543
|
|
|
|
|
|
|
|
|
544
|
|
|
|
|
|
|
2-dimensional array, format_pretty([ [1, 2, ""], [28, "bar", 3], ["foo", 3, |
|
545
|
|
|
|
|
|
|
undef] ]): |
|
546
|
|
|
|
|
|
|
|
|
547
|
|
|
|
|
|
|
+---------+---------+---------+ |
|
548
|
|
|
|
|
|
|
| 1 | 2 | | |
|
549
|
|
|
|
|
|
|
| 28 | bar | 3 | |
|
550
|
|
|
|
|
|
|
| foo | 3 | | |
|
551
|
|
|
|
|
|
|
'---------+---------+---------' |
|
552
|
|
|
|
|
|
|
|
|
553
|
|
|
|
|
|
|
An array of hashrefs, such as commonly found if you use DBI's fetchrow_hashref() |
|
554
|
|
|
|
|
|
|
and friends, format_pretty([ {a=>1, b=>2}, {b=>2, c=>3}, {c=>4} ]): |
|
555
|
|
|
|
|
|
|
|
|
556
|
|
|
|
|
|
|
.-----------. |
|
557
|
|
|
|
|
|
|
| a | b | c | |
|
558
|
|
|
|
|
|
|
+---+---+---+ |
|
559
|
|
|
|
|
|
|
| 1 | 2 | | |
|
560
|
|
|
|
|
|
|
| | 2 | 3 | |
|
561
|
|
|
|
|
|
|
| | | 4 | |
|
562
|
|
|
|
|
|
|
'---+---+---' |
|
563
|
|
|
|
|
|
|
|
|
564
|
|
|
|
|
|
|
Some more complex data, format_pretty({summary => "Blah...", users => |
|
565
|
|
|
|
|
|
|
[{name=>"budi", domains=>["foo.com", "bar.com"], quota=>"1000"}, {name=>"arif", |
|
566
|
|
|
|
|
|
|
domains=>["baz.com"], quota=>"2000"}], verified => 0}): |
|
567
|
|
|
|
|
|
|
|
|
568
|
|
|
|
|
|
|
summary: |
|
569
|
|
|
|
|
|
|
Blah... |
|
570
|
|
|
|
|
|
|
|
|
571
|
|
|
|
|
|
|
users: |
|
572
|
|
|
|
|
|
|
.---------------------------------. |
|
573
|
|
|
|
|
|
|
| domains | name | quota | |
|
574
|
|
|
|
|
|
|
+------------------+------+-------+ |
|
575
|
|
|
|
|
|
|
| foo.com, bar.com | budi | 1000 | |
|
576
|
|
|
|
|
|
|
| baz.com | arif | 2000 | |
|
577
|
|
|
|
|
|
|
'------------------+------+-------' |
|
578
|
|
|
|
|
|
|
|
|
579
|
|
|
|
|
|
|
verified: |
|
580
|
|
|
|
|
|
|
0 |
|
581
|
|
|
|
|
|
|
|
|
582
|
|
|
|
|
|
|
Structures which can't be handled yet will simply be output as YAML, |
|
583
|
|
|
|
|
|
|
format_pretty({a {b=>1}}): |
|
584
|
|
|
|
|
|
|
|
|
585
|
|
|
|
|
|
|
--- |
|
586
|
|
|
|
|
|
|
a: |
|
587
|
|
|
|
|
|
|
b: 1 |
|
588
|
|
|
|
|
|
|
|
|
589
|
|
|
|
|
|
|
=head1 DESCRIPTION |
|
590
|
|
|
|
|
|
|
|
|
591
|
|
|
|
|
|
|
This module is meant to output data structure in a "pretty" or "nice" format, |
|
592
|
|
|
|
|
|
|
suitable for console programs. The idea of this module is that for you to just |
|
593
|
|
|
|
|
|
|
merrily dump data structure to the console, and this module will figure out how |
|
594
|
|
|
|
|
|
|
to best display your data to the end-user. |
|
595
|
|
|
|
|
|
|
|
|
596
|
|
|
|
|
|
|
Currently this module tries to display the data mostly as a nice text table (or |
|
597
|
|
|
|
|
|
|
a series of text tables), and failing that, display it as YAML. |
|
598
|
|
|
|
|
|
|
|
|
599
|
|
|
|
|
|
|
This module takes piping into consideration, and will output a simpler, more |
|
600
|
|
|
|
|
|
|
suitable format when your user pipes your program's output into some other |
|
601
|
|
|
|
|
|
|
program. |
|
602
|
|
|
|
|
|
|
|
|
603
|
|
|
|
|
|
|
Most of the time, you don't have to configure anything, but some options are |
|
604
|
|
|
|
|
|
|
provided to tweak the output. |
|
605
|
|
|
|
|
|
|
|
|
606
|
|
|
|
|
|
|
=for Pod::Coverage ^(content_type)$ |
|
607
|
|
|
|
|
|
|
|
|
608
|
|
|
|
|
|
|
=head1 FUNCTIONS |
|
609
|
|
|
|
|
|
|
|
|
610
|
|
|
|
|
|
|
=for Pod::Coverage new |
|
611
|
|
|
|
|
|
|
|
|
612
|
|
|
|
|
|
|
=head2 format_pretty($data, \%opts) |
|
613
|
|
|
|
|
|
|
|
|
614
|
|
|
|
|
|
|
Return formatted data structure. Options: |
|
615
|
|
|
|
|
|
|
|
|
616
|
|
|
|
|
|
|
=over |
|
617
|
|
|
|
|
|
|
|
|
618
|
|
|
|
|
|
|
=item * interactive => BOOL (optional, default undef) |
|
619
|
|
|
|
|
|
|
|
|
620
|
|
|
|
|
|
|
If set, will override interactive terminal detection (-t STDOUT). Simpler |
|
621
|
|
|
|
|
|
|
formatting will be done if terminal is non-interactive (e.g. when output is |
|
622
|
|
|
|
|
|
|
piped). Using this option will force simpler/full formatting. |
|
623
|
|
|
|
|
|
|
|
|
624
|
|
|
|
|
|
|
=item * list_max_columns => INT |
|
625
|
|
|
|
|
|
|
|
|
626
|
|
|
|
|
|
|
When displaying list as columns, specify maximum number of columns. This can be |
|
627
|
|
|
|
|
|
|
used to force fewer columns (for example, single column) instead of using the |
|
628
|
|
|
|
|
|
|
whole available terminal width. |
|
629
|
|
|
|
|
|
|
|
|
630
|
|
|
|
|
|
|
=item * table_column_orders => [[COLNAME1, COLNAME2], ...] |
|
631
|
|
|
|
|
|
|
|
|
632
|
|
|
|
|
|
|
Specify column orders when drawing a table. If a table has all the columns, then |
|
633
|
|
|
|
|
|
|
the column names will be ordered according to the specification. For example, |
|
634
|
|
|
|
|
|
|
when table_column_orders is [[qw/foo bar baz/]], this table's columns will not |
|
635
|
|
|
|
|
|
|
be reordered because it doesn't have all the mentioned columns: |
|
636
|
|
|
|
|
|
|
|
|
637
|
|
|
|
|
|
|
|foo|quux| |
|
638
|
|
|
|
|
|
|
|
|
639
|
|
|
|
|
|
|
But this table will: |
|
640
|
|
|
|
|
|
|
|
|
641
|
|
|
|
|
|
|
|apple|bar|baz|foo|quux| |
|
642
|
|
|
|
|
|
|
|
|
643
|
|
|
|
|
|
|
into: |
|
644
|
|
|
|
|
|
|
|
|
645
|
|
|
|
|
|
|
|apple|foo|bar|baz|quux| |
|
646
|
|
|
|
|
|
|
|
|
647
|
|
|
|
|
|
|
=item * table_column_formats => [{COLNAME=>FMT, ...}, ...] |
|
648
|
|
|
|
|
|
|
|
|
649
|
|
|
|
|
|
|
Specify formats for columns. Each table format specification is a hashref |
|
650
|
|
|
|
|
|
|
{COLNAME=>FMT, COLNAME2=>FMT2, ...}. It will be applied to a table if the table |
|
651
|
|
|
|
|
|
|
has all the columns. FMT is a format specification according to |
|
652
|
|
|
|
|
|
|
L<Data::Unixish::Apply>, it's basically either a name of a dux function (e.g. |
|
653
|
|
|
|
|
|
|
C<"date">) or an array of function name + arguments (e.g. C<< [['date', [align |
|
654
|
|
|
|
|
|
|
=> {align=>'middle'}]] >>). This will be fed to L<Text::ANSITable>'s C<formats> |
|
655
|
|
|
|
|
|
|
column style. |
|
656
|
|
|
|
|
|
|
|
|
657
|
|
|
|
|
|
|
=item * table_column_types => [{COLNAME=>TYPE, ...}, ...] |
|
658
|
|
|
|
|
|
|
|
|
659
|
|
|
|
|
|
|
Specify types for columns. Each table format specification is a hashref |
|
660
|
|
|
|
|
|
|
{COLNAME=>TYPE, COLNAME2=>TYPE2, ...}. It will be applied to a table if the |
|
661
|
|
|
|
|
|
|
table has all the columns. TYPE is type name according to L<Sah> schema. This |
|
662
|
|
|
|
|
|
|
will be fed to L<Text::ANSITable>'s C<type> column style to give hints on how to |
|
663
|
|
|
|
|
|
|
format the column. Sometimes this is the simpler alternative to |
|
664
|
|
|
|
|
|
|
C<table_column_formats>. |
|
665
|
|
|
|
|
|
|
|
|
666
|
|
|
|
|
|
|
=back |
|
667
|
|
|
|
|
|
|
|
|
668
|
|
|
|
|
|
|
=head1 ENVIRONMENT |
|
669
|
|
|
|
|
|
|
|
|
670
|
|
|
|
|
|
|
=over |
|
671
|
|
|
|
|
|
|
|
|
672
|
|
|
|
|
|
|
=item * INTERACTIVE => BOOL |
|
673
|
|
|
|
|
|
|
|
|
674
|
|
|
|
|
|
|
To set default for C<interactive> option (overrides automatic detection). |
|
675
|
|
|
|
|
|
|
|
|
676
|
|
|
|
|
|
|
=item * FORMAT_PRETTY_LIST_MAX_COLUMNS => INT |
|
677
|
|
|
|
|
|
|
|
|
678
|
|
|
|
|
|
|
To set C<list_max_columns> option. |
|
679
|
|
|
|
|
|
|
|
|
680
|
|
|
|
|
|
|
=item * FORMAT_PRETTY_TABLE_COLUMN_FORMATS => ARRAY (JSON) |
|
681
|
|
|
|
|
|
|
|
|
682
|
|
|
|
|
|
|
To set C<table_column_formats> option, interpreted as JSON. |
|
683
|
|
|
|
|
|
|
|
|
684
|
|
|
|
|
|
|
=item * FORMAT_PRETTY_TABLE_COLUMN_TYPES => ARRAY (JSON) |
|
685
|
|
|
|
|
|
|
|
|
686
|
|
|
|
|
|
|
To set C<table_column_types> option, interpreted as JSON. |
|
687
|
|
|
|
|
|
|
|
|
688
|
|
|
|
|
|
|
=item * FORMAT_PRETTY_TABLE_COLUMN_ORDERS => ARRAY (JSON) |
|
689
|
|
|
|
|
|
|
|
|
690
|
|
|
|
|
|
|
To set C<table_column_orders> option, interpreted as JSON. |
|
691
|
|
|
|
|
|
|
|
|
692
|
|
|
|
|
|
|
=item * COLUMNS => INT |
|
693
|
|
|
|
|
|
|
|
|
694
|
|
|
|
|
|
|
To override terminal width detection. |
|
695
|
|
|
|
|
|
|
|
|
696
|
|
|
|
|
|
|
=back |
|
697
|
|
|
|
|
|
|
|
|
698
|
|
|
|
|
|
|
=head1 HOMEPAGE |
|
699
|
|
|
|
|
|
|
|
|
700
|
|
|
|
|
|
|
Please visit the project's homepage at L<https://metacpan.org/release/Data-Format-Pretty-Console>. |
|
701
|
|
|
|
|
|
|
|
|
702
|
|
|
|
|
|
|
=head1 SOURCE |
|
703
|
|
|
|
|
|
|
|
|
704
|
|
|
|
|
|
|
Source repository is at L<https://github.com/perlancar/perl-Data-Format-Pretty-Console>. |
|
705
|
|
|
|
|
|
|
|
|
706
|
|
|
|
|
|
|
=head1 BUGS |
|
707
|
|
|
|
|
|
|
|
|
708
|
|
|
|
|
|
|
Please report any bugs or feature requests on the bugtracker website L<https://rt.cpan.org/Public/Dist/Display.html?Name=Data-Format-Pretty-Console> |
|
709
|
|
|
|
|
|
|
|
|
710
|
|
|
|
|
|
|
When submitting a bug or request, please include a test-file or a |
|
711
|
|
|
|
|
|
|
patch to an existing test-file that illustrates the bug or desired |
|
712
|
|
|
|
|
|
|
feature. |
|
713
|
|
|
|
|
|
|
|
|
714
|
|
|
|
|
|
|
=head1 SEE ALSO |
|
715
|
|
|
|
|
|
|
|
|
716
|
|
|
|
|
|
|
Modules used for formatting: L<Text::ANSITable>, L<YAML>. |
|
717
|
|
|
|
|
|
|
|
|
718
|
|
|
|
|
|
|
L<Data::Format::Pretty> |
|
719
|
|
|
|
|
|
|
|
|
720
|
|
|
|
|
|
|
=head1 AUTHOR |
|
721
|
|
|
|
|
|
|
|
|
722
|
|
|
|
|
|
|
perlancar <perlancar@cpan.org> |
|
723
|
|
|
|
|
|
|
|
|
724
|
|
|
|
|
|
|
=head1 COPYRIGHT AND LICENSE |
|
725
|
|
|
|
|
|
|
|
|
726
|
|
|
|
|
|
|
This software is copyright (c) 2017, 2016, 2015, 2014, 2013, 2012, 2011, 2010 by perlancar@cpan.org. |
|
727
|
|
|
|
|
|
|
|
|
728
|
|
|
|
|
|
|
This is free software; you can redistribute it and/or modify it under |
|
729
|
|
|
|
|
|
|
the same terms as the Perl 5 programming language system itself. |
|
730
|
|
|
|
|
|
|
|
|
731
|
|
|
|
|
|
|
=cut |