File Coverage

blib/lib/Data/Dump/Partial.pm
Criterion Covered Total %
statement 100 101 99.0
branch 52 54 96.3
condition 47 50 94.0
subroutine 8 9 88.8
pod 2 2 100.0
total 209 216 96.7


line stmt bran cond sub pod time code
1             package Data::Dump::Partial;
2              
3 2     2   459819 use 5.010001;
  2         9  
4 2     2   16 use strict;
  2         4  
  2         68  
5 2     2   18 use warnings;
  2         4  
  2         148  
6 2     2   1373 use experimental 'smartmatch';
  2         7856  
  2         18  
7 2     2   1758 use Data::Dump::Filtered;
  2         22688  
  2         7439  
8              
9             require Exporter;
10             our @ISA = qw(Exporter);
11             our @EXPORT_OK = qw(dump_partial dumpp);
12              
13             our $AUTHORITY = 'cpan:PERLANCAR'; # AUTHORITY
14             our $DATE = '2024-01-02'; # DATE
15             our $DIST = 'Data-Dump-Partial'; # DIST
16             our $VERSION = '0.070'; # VERSION
17              
18 0     0   0 sub _dmp { Data::Dump::Filtered::dump_filtered(@_, undef) }
19              
20             sub dump_partial {
21 55     55 1 371249 my @data = @_;
22 55 50 66     217 die 'Usage: dump_partial(@data, \%opts)'
23             if @data > 1 && ref($data[-1]) ne 'HASH';
24 55 100       115 my $opts = (@data > 1) ? {%{pop(@data)}} : {};
  43         136  
25              
26 55   100     173 $opts->{max_keys} //= 5;
27 55   100     142 $opts->{max_elems} //= 5;
28 55   100     172 $opts->{max_len} //= 32;
29 55   100     118 $opts->{max_total_len} //= 80;
30              
31 1         2 $opts->{max_keys} = @{$opts->{precious_keys}} if $opts->{precious_keys} &&
32 55 100 100     100 @{ $opts->{precious_keys} } > $opts->{max_keys};
  6         16  
33              
34 55         64 my $out;
35              
36 55 100       96 if ($opts->{_inner}) {
37             #print "DEBUG: inner dump, data="._dmp(@data)."\n";
38 23         47 $out = Data::Dump::dump(@data);
39             } else {
40             #print "DEBUG: outer dump, data="._dmp(@data)."\n";
41             my $filter = sub {
42 154     154   10768 my ($ctx, $oref) = @_;
43              
44             # to avoid deep recursion (dump_partial keeps modifying the hash due
45             # to pair_filter or mask_keys_regex)
46 154         172 my $skip_modify_outermost_hash;
47 154 100       284 if ($opts->{_skip_modify_outermost_hash}) {
48             #print "DEBUG: Will skip modify outermost hash\n";
49 17         24 $skip_modify_outermost_hash++;
50 17         25 $opts->{_skip_modify_outermost_hash}--;
51             }
52              
53 154 100 100     412 if ($opts->{max_len} && $ctx->is_scalar && defined($$oref) &&
    100 66        
    100 100        
      100        
      100        
54             length($$oref) > $opts->{max_len}) {
55              
56             #print "DEBUG: truncating scalar\n";
57 4         40 return { object => substr($$oref, 0, $opts->{max_len}-3)."..." };
58              
59             } elsif ($opts->{max_elems} && $ctx->is_array &&
60             @$oref > $opts->{max_elems}) {
61              
62             #print "DEBUG: truncating array\n";
63 6         79 my @ary = @{$oref}[0..($opts->{max_elems}-1)];
  6         42  
64 6         14 local $opts->{_inner} = 1;
65 6         10 local $opts->{max_total_len} = 0;
66 6         14 my $out = dump_partial(\@ary, $opts);
67 6         35 $out =~ s/(?:, )?]$/, ...]/;
68 6         34 return { dump => $out };
69              
70             } elsif ($ctx->is_hash) {
71              
72 38         520 my %hash;
73             my $modified;
74              
75 38 100 100     188 if ($opts->{pair_filter} && !$skip_modify_outermost_hash) {
76 5         17 for (sort keys %$oref) {
77 11         20 my @res = $opts->{pair_filter}->($_, $oref->{$_});
78 11 100 66     120 $modified = "pair_filter" unless @res == 2 &&
      100        
79             $res[0] eq $_ && "$res[1]" eq "$oref->{$_}";
80 11         27 while (my ($k, $v) = splice @res, 0, 2) {
81 11         40 $hash{$k} = $v;
82             }
83             }
84             } else {
85 33         144 %hash = %$oref;
86             }
87              
88 38 100 100     94 if ($opts->{mask_keys_regex} && !$skip_modify_outermost_hash) {
89 3         11 for (sort keys %hash) {
90 7 100       30 if (/$opts->{mask_keys_regex}/) {
91 3         4 $modified = "mask_keys_regex";
92 3         6 $hash{$_} = '***';
93             }
94             }
95             }
96              
97 38         42 my $truncated;
98 38 100 100     113 if ($opts->{max_keys} && keys(%$oref) > $opts->{max_keys}) {
99 9         12 my $mk = $opts->{max_keys};
100             {
101 9 100       10 if ($opts->{hide_keys}) {
  9         18  
102 1         4 for my $k (sort keys %hash) {
103 6 100       7 delete $hash{$k} if grep { $_ eq $k } @{$opts->{hide_keys}};
  18         28  
  6         8  
104             }
105             }
106 9 100       17 last if keys(%hash) <= $mk;
107 8 100       43 if ($opts->{worthless_keys}) {
108 1         5 for my $k (sort keys %hash) {
109 6 100       11 last if keys(%hash) <= $mk;
110 5 100       5 delete $hash{$k} if grep { $_ eq $k } @{$opts->{worthless_keys}};
  15         22  
  5         7  
111             }
112             }
113 8 100       17 last if keys(%hash) <= $mk;
114 7         42 for my $k (reverse sort keys %hash) {
115             delete $hash{$k} if !$opts->{precious_keys} ||
116 20 100 100     37 !(grep { $_ eq $k } @{$opts->{precious_keys}});
  32         83  
  11         15  
117 20 100       36 last if keys(%hash) <= $mk;
118             }
119             }
120 9         15 $modified = "truncate";
121 9         14 $truncated++;
122             }
123              
124 38 100       88 if ($modified) {
125             #print "DEBUG: modified hash ($modified)\n";
126 17         34 local $opts->{_inner} = 1;
127 17         23 local $opts->{_skip_modify_outermost_hash} = 1;
128 17         27 local $opts->{max_total_len} = 0;
129 17         32 my $out = dump_partial(\%hash, $opts);
130 17 100       64 $out =~ s/(?:, )? }$/, ... }/ if $truncated;
131 17         103 return { dump => $out };
132             }
133             }
134              
135 127 100       1574 if ($opts->{dd_filter}) {
136 1         3 return $opts->{dd_filter}->($ctx, $oref);
137             } else {
138 126         277 return;
139             }
140 32         197 };
141 32         125 $out = Data::Dump::Filtered::dump_filtered(@data, $filter);
142             }
143              
144 55         5817 for ($out) {
145 55         109 s/^\s*#.*//mg; # comments
146 55         129 s/^\s+//mg; # indents
147 55         138 s/\n+/ /g; # newlines
148             }
149              
150 55 100 100     203 if ($opts->{max_total_len} && length($out) > $opts->{max_total_len}) {
151 1         5 $out = substr($out, 0, $opts->{max_total_len}-3) . "...";
152             }
153              
154 55 50       96 print STDERR "$out\n" unless defined wantarray;
155 55         336 $out;
156             }
157              
158 31     31 1 76 sub dumpp { dump_partial(@_) }
159              
160             1;
161             # ABSTRACT: Dump data structure compactly and potentially partially
162              
163             __END__