File Coverage

lib/Data/Pretty.pm
Criterion Covered Total %
statement 378 400 94.5
branch 195 234 83.3
condition 106 144 73.6
subroutine 22 24 91.6
pod 5 9 55.5
total 706 811 87.0


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