File Coverage

lib/Data/Pretty.pm
Criterion Covered Total %
statement 381 401 95.0
branch 196 232 84.4
condition 102 138 73.9
subroutine 23 25 92.0
pod 5 9 55.5
total 707 805 87.8


line stmt bran cond sub pod time code
1             ##----------------------------------------------------------------------------
2             ## Data Dump Beautifier - ~/lib/Data/Pretty.pm
3             ## Version v0.1.7
4             ## Copyright(c) 2023 DEGUEST Pte. Ltd.
5             ## Author: Jacques Deguest
6             ## Created 2023/08/06
7             ## Modified 2023/09/25
8             ## All rights reserved
9             ##
10             ##
11             ## This program is free software; you can redistribute it and/or modify it
12             ## under the same terms as Perl itself.
13             ##----------------------------------------------------------------------------
14             package Data::Pretty;
15             BEGIN
16             {
17 16     16   1756706 use strict;
  16         196  
  16         500  
18 16     16   100 use warnings;
  16         44  
  16         668  
19 16         2541 use vars qw(
20             @EXPORT @EXPORT_OK $VERSION $DEBUG
21             %seen %refcnt @dump @fixup %require
22             $TRY_BASE64 @FILTERS $INDENT $LINEWIDTH $SHOW_UTF8 $CODE_DEPARSE
23 16     16   92 );
  16         34  
24 16     16   9539 use subs qq(dump);
  16         588  
  16         89  
25 16     16   22149 use overload ();
  16         16675  
  16         1260  
26 16     16   119 require Exporter;
27 16         91 *import = \&Exporter::import;
28 16         62 @EXPORT = qw( dd ddx );
29 16         49 @EXPORT_OK = qw( dump pp dumpf literal quote );
30 16         25 our $DEBUG = 0;
31 16         373 our $VERSION = 'v0.1.7';
32             };
33              
34 16     16   82 use strict;
  16         27  
  16         328  
35 16     16   67 use warnings;
  16         27  
  16         1446  
36              
37             $TRY_BASE64 = 50 unless defined $TRY_BASE64;
38             $INDENT = ' ' unless defined $INDENT;
39             $LINEWIDTH = 60 unless defined $LINEWIDTH;
40             $SHOW_UTF8 = 1 unless defined $SHOW_UTF8;
41             $CODE_DEPARSE = 1 unless defined $CODE_DEPARSE;
42              
43             {
44 16     16   109 no warnings 'once';
  16         31  
  16         56170  
45             *pp = \&dump;
46             }
47              
48             sub dd {
49 1     1 1 2253 print dump(@_), "\n";
50             }
51              
52             sub ddx {
53 2     2 1 53 my(undef, $file, $line) = caller;
54 2         14 $file =~ s,.*[\\/],,;
55 2         7 my $out = "$file:$line: " . dump(@_) . "\n";
56 2         404 $out =~ s/^/# /gm;
57 2         124 print $out;
58             }
59              
60             sub dump
61             {
62 112     112   29214 local %seen;
63 112         163 local %refcnt;
64 112         138 local %require;
65 112         169 local @fixup;
66              
67 112 100       734 require Data::Pretty::FilterContext if @FILTERS;
68              
69 112         166 my $name = "a";
70 112         148 my @dump;
71              
72 112         312 my $use_qw = &_use_qw( [@_] );
73 112         249 for my $v (@_) {
74             # my $val = _dump($v, $name, [], tied($v));
75 160         382 my $val = _dump(
76             $v,
77             name => $name,
78             idx => [],
79             dont_remember => tied($v),
80             use_qw => $use_qw,
81             );
82 160         384 push(@dump, [$name, $val]);
83             } continue {
84 160         293 $name++;
85             }
86              
87 112         169 my $out = "";
88 112 100       252 if (%require) {
89 5         30 for (sort keys %require) {
90 5         30 $out .= "require $_;\n";
91             }
92             }
93 112 100       226 if (%refcnt) {
94             # output all those with refcounts first
95 8         24 for (@dump) {
96 16         26 my $name = $_->[0];
97 16 100       62 if ($refcnt{$name}) {
98 8         28 $out .= "my \$$name = $_->[1];\n";
99 8         32 undef $_->[1];
100             }
101             }
102 8         23 for (@fixup) {
103 15         152 $out .= "$_;\n";
104             }
105             }
106              
107 112         186 my $paren = (@dump != 1);
108             my $formatted = format_list(
109             paren => $paren,
110             comment => undef,
111 112 100       270 values => [map {defined($_->[1]) ? $_->[1] : "\$" .$_->[0]} @dump],
  160         628  
112             use_qw => $use_qw,
113             );
114 112         314 my $has_qw = substr( $formatted, 0, 2 ) eq 'qw';
115 112 100 100     253 $out .= "(" if( $paren && !$has_qw );
116 112         177 $out .= $formatted;
117 112 100 100     259 $out .= ")" if( $paren && !$has_qw );
118              
119 112 100 100     379 if (%refcnt || %require) {
120 12         24 $out .= ";\n";
121 12         496 $out =~ s/^/$INDENT/gm;
122 12         83 $out = "do {\n$out}";
123             }
124              
125 112 50       228 print STDERR "$out\n" unless defined wantarray;
126 112         1773 $out;
127             }
128              
129             sub dumpf {
130 8     8 1 1699 require Data::Pretty::Filtered;
131 8         46 goto &Data::Pretty::Filtered::dump_filtered;
132             }
133              
134             sub format_list
135             {
136 130     130 0 448 my $opts = {@_};
137 130         237 my $paren = $opts->{paren};
138 130         178 my $comment = $opts->{comment};
139 130 100       226 my $indent_lim = $paren ? 0 : 1;
140 130 50       1060 my $use_qw = defined( $opts->{use_qw} ) ? $opts->{use_qw} : 1;
141 130         199 my $values = $opts->{values};
142            
143 130 100       314 if (@$values > 3) {
144             # my $use_quotes = 0;
145             # can we use range operator to shorten the list?
146 12         18 my $i = 0;
147 12         34 while ($i < @$values) {
148 40         44 my $j = $i + 1;
149 40         54 my $v = $values->[$i];
150 40         68 while ($j < @$values) {
151             # NOTE: allow string increment too?
152 316 100 100     1335 if ($v eq "0" || $v =~ /^-?[1-9]\d{0,9}\z/) {
    100          
153 40         57 $v++;
154             }
155             elsif ($v =~ /^"([A-Za-z]{1,3}\d*)"\z/) {
156 266         432 $v = $1;
157 266         279 $v++;
158 266         313 $v = qq("$v");
159             }
160             else {
161 10         19 last;
162             }
163 306 100       474 last if $values->[$j] ne $v;
164 289         416 $j++;
165             }
166 40 100       102 if ($j - $i > 3) {
167 9         79 splice(@$values, $i, $j - $i, "$values->[$i] .. $values->[$j-1]");
168 9         15 $use_qw = 0;
169             }
170 40         74 $i++;
171             }
172             }
173              
174 130 100       255 if( $use_qw )
175             {
176 1         1 my @repl;
177 1         2 foreach my $v ( @$values )
178             {
179 3         13 ( my $v2 = $v ) =~ s/^\"|\"$//g;
180 3         6 push( @repl, $v2 );
181             }
182 1         3 @$values = @repl;
183             }
184              
185 130         297 my $tmp = "@$values";
186 130 100       246 my $sep = $use_qw ? ' ' : ', ';
187 130 100 33     662 if ($comment || (@$values > $indent_lim && (length($tmp) > $LINEWIDTH || $tmp =~ /\n/))) {
      66        
      66        
188 1 50       12 if( $use_qw )
189             {
190 0         0 my @lines;
191             my @buf;
192 0         0 foreach my $v ( @$values )
193             {
194 0 0 0     0 if( scalar( @buf ) && length( $INDENT . join( ' ', @buf, $v ) ) > $LINEWIDTH )
195             {
196 0         0 push( @lines, $INDENT . join( ' ', @buf ) );
197 0         0 @buf = ( $v );
198             }
199             else
200             {
201 0         0 push( @buf, $v );
202             }
203             }
204 0 0       0 push( @lines, $INDENT . join( ' ', @buf ) ) if( scalar( @buf ) );
205             return (
206 0 0       0 $comment
    0          
    0          
207             ? ( scalar( @lines ) > 1 ? "\n$INDENT" : '' ) . "# $comment" . ( scalar( @lines ) > 1 ? "\n" : '' )
208             : ''
209             ) . 'qw(' . "\n" . join("\n", @lines,"") . ')';
210             }
211             else
212             {
213 1         6 my @elem = @$values;
214 1         3 for (@elem) { s/^/$INDENT/gm; }
  1         5  
215 1 50       12 return "\n" . ($comment ? "$INDENT# $comment\n" : "") .
216             join(",\n", @elem, "");
217             }
218             } else {
219 129 100       557 return $use_qw ? 'qw( ' . join( $sep, @$values ) . ' )' : join($sep, @$values);
220             }
221             }
222              
223             sub fullname
224             {
225 28     28 0 63 my($name, $idx, $ref) = @_;
226 28         55 substr($name, 0, 0) = "\$";
227              
228 28         54 my @i = @$idx; # need copy in order to not modify @$idx
229 28 0 33     84 if ($ref && @i && $i[0] eq "\$") {
      33        
230 0         0 shift(@i); # remove one deref
231 0         0 $ref = 0;
232             }
233 28   100     117 while (@i && $i[0] eq "\$") {
234 1         2 shift @i;
235 1         3 $name = "\$$name";
236             }
237              
238 28         51 my $last_was_index;
239 28         58 for my $i (@i) {
240 29 100 100     158 if ($i eq "*" || $i eq "\$") {
    100          
241 7         10 $last_was_index = 0;
242 7         15 $name = "$i\{$name}";
243             } elsif ($i =~ s/^\*//) {
244 2         24 $name .= $i;
245 2         6 $last_was_index++;
246             } else {
247 20 100       72 $name .= "->" unless $last_was_index++;
248 20         54 $name .= $i;
249             }
250             }
251 28 50       53 $name = "\\$name" if $ref;
252 28         83 $name;
253             }
254              
255 0     0 1 0 sub literal { return( Data::Pretty::Literal->new( @_ ) ); }
256              
257             my %esc = (
258             "\a" => "\\a",
259             "\b" => "\\b",
260             "\t" => "\\t",
261             "\n" => "\\n",
262             "\f" => "\\f",
263             "\r" => "\\r",
264             "\e" => "\\e",
265             );
266              
267             # put a string value in double quotes
268             sub quote {
269 361     361 1 1370 local($_) = $_[0];
270             # If there are many '"' we might want to use qq() instead
271 361         656 s/([\\\"\@\$])/\\$1/g;
272 361 100       1220 return qq("$_") unless /[^\040-\176]/; # fast exit
273              
274 16         79 s/([\a\b\t\n\f\r\e])/$esc{$1}/g;
275              
276             # no need for 3 digits in escape for these
277 16         45 s/([\0-\037])(?!\d)/sprintf('\\%o',ord($1))/eg;
  1103         2284  
278              
279 16         52 s/([\0-\037\177-\377])/sprintf('\\x%02X',ord($1))/eg;
  393         841  
280 16 100       34 unless( $SHOW_UTF8 )
281             {
282 5         18 $_ =~ s/([^\040-\176])/sprintf('\\x{%X}',ord($1))/eg;
  105         247  
283             }
284              
285 16         117 return qq("$_");
286             }
287              
288             sub str {
289 315     315 0 342 my $opts = $_[1];
290 315 100       499 if (length($_[0]) > 20) {
291 14         34 for ($_[0]) {
292             # Check for repeated string
293 14 100       42 if (/^(.)\1\1\1/s) {
294             # seems to be a repeating sequence, let's check if it really is
295             # without backtracking
296 4 50       51625 unless (/[^\Q$1\E]/) {
297 4         11 my $base = quote($1);
298 4         7 my $repeat = length;
299 4         14 return "($base x $repeat)"
300             }
301             }
302             # Length protection because the RE engine will blow the stack [RT#33520]
303 10 100 66     104 if (length($_) < 16 * 1024 && /^(.{2,5}?)\1*\z/s) {
304 2         6 my $base = quote($1);
305 2         9 my $repeat = length($_)/length($1);
306 2         32 return "($base x $repeat)";
307             }
308             }
309             }
310              
311 309         383 local $_ = "e;
312             # local $_ = $opts->{use_qw} ? $_[0] : "e;
313              
314 309 100 100     620 if (length($_) > 40 && !/\\x\{/ && length($_) > (length($_[0]) * 2)) {
      100        
315             # too much binary data, better to represent as a hex/base64 string
316              
317             # Base64 is more compact than hex when string is longer than
318             # 17 bytes (not counting any require statement needed).
319             # But on the other hand, hex is much more readable.
320 3 100 66     41 if ($TRY_BASE64 && length($_[0]) > $TRY_BASE64 &&
      66        
      100        
      66        
321             (defined &utf8::is_utf8 && !utf8::is_utf8($_[0])) &&
322 1         9 eval { require MIME::Base64 })
323             {
324 1         3 $require{"MIME::Base64"}++;
325 1         62 return "MIME::Base64::decode(\"" .
326             MIME::Base64::encode($_[0],"") .
327             "\")";
328             }
329 2         40 return "pack(\"H*\",\"" . unpack("H*", $_[0]) . "\")";
330             }
331 306         497 return $_;
332             }
333              
334             sub tied_str {
335 80     80 0 107 my $tied = shift;
336 80 100       162 if ($tied) {
337 2 50       7 if (my $tied_ref = ref($tied)) {
338 2         5 $tied = "tied $tied_ref";
339             }
340             else {
341 0         0 $tied = "tied";
342             }
343             }
344 80         106 return $tied;
345             }
346              
347             sub _dump
348             {
349 1409     1409   1895 my $ref = ref $_[0];
350 1409 100       2103 my $rval = $ref ? $_[0] : \$_[0];
351 1409         1533 shift;
352 1409         3649 my $opts = {@_};
353              
354 1409         2891 my($name, $idx, $dont_remember, $pclass, $pidx) = @$opts{qw( name idx dont_remember pclass pidx )};
355              
356 1409         1560 my($class, $type, $id);
357 1409   50     3172 my $strval = overload::StrVal($rval // '');
358             # Parse $strval without using regexps, in order not to clobber $1, $2,...
359 1409 100       5640 if ((my $i = rindex($strval, "=")) >= 0) {
360 26         54 $class = substr($strval, 0, $i);
361 26         46 $strval = substr($strval, $i+1);
362             }
363 1409 50       2328 if ((my $i = index($strval, "(0x")) >= 0) {
364 1409         1819 $type = substr($strval, 0, $i);
365 1409         1738 $id = substr($strval, $i + 2, -1);
366             }
367             else {
368 0   0     0 die "Can't parse " . overload::StrVal($rval // '');
369             }
370 1409 50 33     2305 if ($] < 5.008 && $type eq "SCALAR") {
371 0 0       0 $type = "REF" if $ref eq "REF";
372             }
373 1409 50 0     1897 warn "\$$name(@$idx) ", ( $class || 'undef' ), " $type $id ($ref), strval=$strval" if $DEBUG;
374              
375 1409         2085 my $out;
376             my $comment;
377 1409         0 my $hide_keys;
378 1409 100       2021 if (@FILTERS) {
379 11         13 my $pself = "";
380 11 100       42 $pself = fullname("self", [@$idx[$pidx..(@$idx - 1)]]) if $pclass;
381 11         39 my $ctx = Data::Pretty::FilterContext->new($rval, $class, $type, $ref, $pclass, $pidx, $idx);
382 11         16 my @bless;
383 11         19 for my $filter (@FILTERS) {
384 11 100       23 if (my $f = $filter->($ctx, $rval)) {
385 6 100       34 if (my $v = $f->{object}) {
386 1         2 local @FILTERS;
387 1         34 $out = _dump(
388             $v,
389             name => $name,
390             idx => $idx,
391             dont_remember => 1,
392             );
393 1         2 $dont_remember++;
394             }
395 6 100       19 if (defined(my $c = $f->{bless})) {
396 1         1 push(@bless, $c);
397             }
398 6 100       15 if (my $c = $f->{comment}) {
399 1         2 $comment = $c;
400             }
401 6 100       13 if (defined(my $c = $f->{dump})) {
402 1         2 $out = $c;
403 1         1 $dont_remember++;
404             }
405 6 100       27 if (my $h = $f->{hide_keys}) {
406 2 50       8 if (ref($h) eq "ARRAY") {
407             $hide_keys = sub {
408 2     2   3 for my $k (@$h) {
409 2 100       9 return 1 if $k eq $_[0];
410             }
411 1         3 return 0;
412 2         10 };
413             }
414             }
415             }
416             }
417 11 100 66     38 push(@bless, "") if defined($out) && !@bless;
418 11 100       31 if (@bless) {
419 3         5 $class = shift(@bless);
420 3 50       16 warn "More than one filter callback tried to bless object" if @bless;
421             }
422             }
423              
424 1409 100       1900 unless ($dont_remember) {
425             # We do not use reference alias for scalars because they pose no threat of infinite recursion
426 1394         1322 my $s;
427 1394 100 100     2601 if( ( $s = $seen{$id} ) && $type ne 'SCALAR' ) {
428 8         21 my($sname, $sidx) = @$s;
429 8         15 $refcnt{$sname}++;
430 8   33     39 my $sref = fullname($sname, $sidx,
431             ($ref && $type eq "SCALAR"));
432 8 50       28 warn "SEEN: [\$$name(@$idx)] => [\$$sname(@$sidx)] ($ref,$sref)" if $DEBUG;
433 8 100       35 return $sref unless $sname eq $name;
434 2         3 $refcnt{$name}++;
435 2         5 push(@fixup, fullname($name,$idx)." = $sref");
436 2 100 66     13 return "do{my \$fix}" if @$idx && $idx->[-1] eq '$';
437 1         3 return "'fix'";
438             }
439 1386         4523 $seen{$id} = [$name, $idx];
440             }
441              
442 1401 100       2129 if ($class) {
443 27         32 $pclass = $class;
444 27         39 $pidx = @$idx;
445             }
446              
447 1401 100 100     3797 if (defined $out) {
    100 100        
    100          
    100          
    100          
    100          
    50          
448             # keep it
449             }
450             # NOTE: scalar, ref or regexp
451             elsif ($type eq "SCALAR" || $type eq "REF" || $type eq "REGEXP") {
452 1266 100       1536 if ($ref) {
453             # NOTE: regexp
454 41 100 100     121 if ($class && $class eq "Regexp") {
455 9         35 my $v = "$rval";
456              
457 9         18 my $mod = "";
458 9 50       44 if ($v =~ /^\(\?\^?([msix-]*):([\x00-\xFF]*)\)\z/) {
459 9         16 $mod = $1;
460 9         14 $v = $2;
461 9         14 $mod =~ s/-.*//;
462             }
463              
464 9         10 my $sep = '/';
465 9         16 my $sep_count = ($v =~ tr/\///);
466 9 100       18 if ($sep_count) {
467             # see if we can find a better one
468 4         7 for ('|', ',', ':', '#') {
469 10         457 my $c = eval "\$v =~ tr/\Q$_\E//";
470             #print "SEP $_ $c $sep_count\n";
471 10 100       39 if ($c < $sep_count) {
472 3         5 $sep = $_;
473 3         4 $sep_count = $c;
474 3 50       8 last if $sep_count == 0;
475             }
476             }
477             }
478 9         72 $v =~ s/\Q$sep\E/\\$sep/g;
479              
480 9         22 $out = "qr$sep$v$sep$mod";
481 9         16 undef($class);
482             }
483             else {
484 32 100       75 delete $seen{$id} if $type eq "SCALAR"; # will be seen again shortly
485 32         439 my $val = _dump(
486             $$rval,
487             name => $name,
488             idx => [@$idx, "\$"],
489             dont_remember => 0,
490             pclass => $pclass,
491             pidx => $pidx,
492             );
493 32 100       101 $out = $class ? "do{\\(my \$o = $val)}" : "\\$val";
494             }
495             # NOTE; regular string
496             } else {
497 1225 100   1   2889 if (!defined $$rval) {
  1 100       764  
  1 100       15  
  1         28  
498 3         10 $out = "undef";
499             }
500             elsif ($$rval =~ /^-?(?:nan|inf)/i) {
501 4         8 $out = str($$rval);
502             }
503 16     16   136 elsif (do {no warnings 'numeric'; $$rval + 0 eq $$rval}) {
  16         25  
  16         34184  
  1218         37440  
504 907         955 $out = $$rval;
505             }
506             else {
507 311         487 $out = str($$rval, $opts);
508             # $out = str($$rval);
509             }
510 1225 100 100     2158 if ($class && !@$idx) {
511             # Top is an object, not a reference to one as perl needs
512 1         2 $refcnt{$name}++;
513 1         4 my $obj = fullname($name, $idx);
514 1         2 my $cl = quote($class);
515 1         5 push(@fixup, "bless \\$obj, $cl");
516             }
517             }
518             }
519             # NOTE: glob
520             elsif ($type eq "GLOB") {
521 40 100       63 if ($ref) {
522 6         16 delete $seen{$id};
523 6         44 my $val = _dump(
524             $$rval,
525             name => $name,
526             idx => [@$idx, "*"],
527             dont_remember => 0,
528             pclass => $pclass,
529             pidx => $pidx,
530             );
531 6         15 $out = "\\$val";
532 6 100       24 if ($out =~ /^\\\*Symbol::/) {
533 4         14 $require{Symbol}++;
534 4         6 $out = "Symbol::gensym()";
535             }
536             } else {
537 34         99 my $val = "$$rval";
538 34         67 $out = "$$rval";
539              
540 34         59 for my $k (qw(SCALAR ARRAY HASH)) {
541 102         137 my $gval = *$$rval{$k};
542 102 100       161 next unless defined $gval;
543 41 100 100     126 next if $k eq "SCALAR" && ! defined $$gval; # always there
544 12         14 my $f = scalar @fixup;
545 12         18 push(@fixup, "RESERVED"); # overwritten after _dump() below
546 12         91 $gval = _dump(
547             $gval,
548             name => $name,
549             idx => [@$idx, "*{$k}"],
550             dont_remember => 0,
551             pclass => $pclass,
552             pidx => $pidx,
553             );
554 12         25 $refcnt{$name}++;
555 12         32 my $gname = fullname($name, $idx);
556 12         74 $fixup[$f] = "$gname = $gval"; #XXX indent $gval
557             }
558             }
559             }
560             # NOTE: array
561             elsif ($type eq "ARRAY") {
562 18         35 my @vals;
563 18         49 my $tied = tied_str(tied(@$rval));
564             # Quick check if we are dealing with a simple array of words/terms
565             # and thus if we can use qw( .... ) instead of ( "some", "thing", "else" )
566 18         44 my $use_qw = &_use_qw( $rval );
567              
568 18         45 my $i = 0;
569 18         51 for my $v (@$rval) {
570 291         868 push(@vals, _dump(
571             $v,
572             name => $name,
573             idx => [@$idx, "[$i]"],
574             dont_remember => $tied,
575             pclass => $pclass,
576             pidx => $pidx,
577             use_qw => $use_qw,
578             ));
579 291         428 $i++;
580             }
581 18         87 $out = "[" . format_list(
582             paren => 1,
583             comment => $tied,
584             values => \@vals,
585             use_qw => $use_qw,
586             ) . "]";
587             }
588             # NOTE: hash
589             elsif ($type eq "HASH") {
590 62         83 my(@keys, @vals);
591 62         183 my $tied = tied_str(tied(%$rval));
592              
593             # statistics to determine variation in key lengths
594 62         79 my $kstat_max = 0;
595 62         75 my $kstat_sum = 0;
596 62         77 my $kstat_sum2 = 0;
597              
598 62         534 my @orig_keys = keys %$rval;
599 62 100       156 if ($hide_keys) {
600 1         10 @orig_keys = grep !$hide_keys->($_), @orig_keys;
601             }
602 62         74 my $text_keys = 0;
603 62         103 for (@orig_keys) {
604 50 100       291 $text_keys++, last unless /^[-+]?(?:0|[1-9]\d*)(?:\.\d+)?\z/;
605             }
606              
607 62 100       116 if ($text_keys) {
608 43         161 @orig_keys = sort { lc($a) cmp lc($b) } @orig_keys;
  4951         5413  
609             }
610             else {
611 19         39 @orig_keys = sort { $a <=> $b } @orig_keys;
  5         14  
612             }
613              
614             # my $quote;
615 62         101 my $need_quotes = {};
616 62         156 for my $key (@orig_keys) {
617 907 100       1947 next if $key =~ /^-?[a-zA-Z_]\w*\z/;
618 33 100       92 next if $key =~ /^-?[1-9]\d{0,8}\z/;
619 29 100       69 next if $key =~ /^-?\d{1,9}\.\d+\z/;
620             # $quote++;
621 26         51 $need_quotes->{ $key }++;
622             # last;
623             }
624              
625 62         98 my $need_breakdown = 0;
626 62         110 for my $key (@orig_keys) {
627 907         1026 my $orig = $key;
628 907         1390 my $val = \$rval->{$key}; # capture value before we modify $key
629             # $key = quote($key) if $quote;
630 907 100       1360 $key = quote($key) if $need_quotes->{ $key };
631 907 100       1265 $kstat_max = length($key) if length($key) > $kstat_max;
632 907         941 $kstat_sum += length($key);
633 907         1005 $kstat_sum2 += length($key) * length($key);
634              
635 907         1135 push(@keys, $key);
636             # push(@vals, _dump($$val, $name, [@$idx, "{$key}"], $tied, $pclass, $pidx));
637 907         3285 my $this = _dump(
638             $$val,
639             name => $name,
640             idx => [@$idx, "{$key}"],
641             dont_remember => $tied,
642             pclass => $pclass,
643             pidx => $pidx,
644             );
645 907         998 my $this_type;
646 907 100 50     1995 if ((my $i = index(overload::StrVal($$val // ''), "(0x")) >= 0) {
647 47   50     275 $this_type = substr(overload::StrVal($$val // ''), 0, $i);
648             }
649             # Our child element is also an HASH, and if it is not empty, this would become too much of a cluttered structure to print in just one line.
650 907 100 100     3745 if( defined( $this_type ) && $this_type eq 'HASH' && scalar( keys( %{$rval->{$orig}} ) ) )
  26   100     80  
651             {
652 15         18 $need_breakdown++;
653             }
654 907         1758 push( @vals, $this );
655             }
656 62         94 my $nl = "";
657 62         86 my $klen_pad = 0;
658 62         426 my $tmp = "@keys @vals";
659 62 100 66     376 if (length($tmp) > $LINEWIDTH || $tmp =~ /\n/ || $tied || $need_breakdown) {
      100        
      66        
660 23         37 $nl = "\n";
661             }
662 62         111 $out = "{$nl";
663 62 100       144 $out .= "$INDENT# $tied$nl" if $tied;
664 62         137 while (@keys) {
665 907         1063 my $key = shift @keys;
666 907         988 my $val = shift @vals;
667 907 50       1407 my $vpad = $INDENT . (" " x ($klen_pad ? $klen_pad + 4 : 0));
668 907         1768 $val =~ s/\n/\n$vpad/gm;
669 907 100       1098 my $kpad = $nl ? $INDENT : " ";
670 907 50 66     1965 $key .= " " x ($klen_pad - length($key)) if $nl && $klen_pad > length($key);
671 907         2008 $out .= "$kpad$key => $val,$nl";
672             }
673 62 100       219 $out =~ s/,$/ / unless $nl;
674 62         189 $out .= "}";
675             }
676             # NOTE: code
677             elsif ($type eq "CODE") {
678 2 100 66     23 if( $CODE_DEPARSE && eval { require B::Deparse } )
  1         9  
679             {
680             # -sC to cuddle elsif, else and continue
681             # -si4 indent by 4 spaces (default)
682             # -p use extra parenthesis
683             # my $deparse = B::Deparse->new("-p", "-sC");
684 1         49 my $deparse = B::Deparse->new;
685 1         2928 my $code = $deparse->coderef2text( $rval );
686             # Don't let our environment influence the code
687 1         31 1 while $code =~ s/^\{[\s\n]+use\s(warnings|strict);\n/\{\n/gs;
688 1         10 $out = 'sub ' . $code;
689             }
690             else
691             {
692 1         3 $out = 'sub { ... }';
693             }
694             }
695             # NOTE: vstring
696             elsif ($type eq "VSTRING") {
697 11 100       48 $out = sprintf +($ref ? '\v%vd' : 'v%vd'), $$rval;
698             }
699             # NOTE: other type unsupported
700             else {
701 0         0 warn "Can't handle $type data";
702 0         0 $out = "'#$type#'";
703             }
704              
705 1401 100 100     2128 if ($class && $ref) {
706 14 50       43 if( $class eq 'Data::Pretty::Literal' )
707             {
708 0         0 $out = $$rval;
709             }
710             else
711             {
712 14         45 $out = "bless($out, " . quote($class) . ")";
713             }
714             }
715 1401 100       1867 if ($comment) {
716 1         14 $comment =~ s/^/# /gm;
717 1 50       7 $comment .= "\n" unless $comment =~ /\n\z/;
718 1         6 $comment =~ s/^#[ \t]+\n/\n/;
719 1         2 $out = "$comment$out";
720             }
721 1401         3544 return $out;
722             }
723              
724             sub _use_qw
725             {
726 130     130   208 my $rval = shift( @_ );
727             # Quick check if we are dealing with a simple array of words/terms
728             # and thus if we can use qw( .... ) instead of ( "some", "thing", "else" )
729 130         164 my $use_qw = 1;
730 130         156 my $only_numbers = 0;
731 130         223 foreach my $v ( @$rval )
732             {
733 433 100 100     1581 if( !defined( $v ) ||
      100        
      66        
      100        
734             ref( $v ) ||
735             substr( overload::StrVal( \$v ), 0, 7 ) eq 'VSTRING' ||
736             # See perlop/"qw/STRING/" section
737             ( !ref( $v ) && $v =~ /[\,\\\#[:blank:]\h\v\a\b\t\n\f\r\e\@\"\$]/ ) )
738             {
739 72         216 $use_qw = 0;
740 72         104 last;
741             }
742 361 100       3160 $only_numbers++ if( $v =~ /^[-+]?(?:0|[1-9]\d*)(?:\.\d+)?\z/ );
743             }
744             # Don't use qw() if we are only dealing with numbers
745 130 100 100     496 $use_qw = 0 if( $only_numbers == scalar( @$rval ) || scalar( @$rval ) == 1 );
746 130         228 return( $use_qw );
747             }
748              
749             {
750             package
751             Data::Pretty::Literal;
752             sub new
753             {
754 0     0   0 my $this = shift( @_ );
755 0         0 my $str = shift( @_ );
756 0 0 0     0 return( bless( ( ref( $str ) eq 'SCALAR' ? $str : \$str ) => ( ref( $this ) || $this ) ) );
757             }
758             }
759              
760             1;
761             # NOTE: POD
762             __END__