File Coverage

blib/lib/Data/Difflet.pm
Criterion Covered Total %
statement 101 108 93.5
branch 29 36 80.5
condition 7 9 77.7
subroutine 16 17 94.1
pod 2 3 66.6
total 155 173 89.6


line stmt bran cond sub pod time code
1             package Data::Difflet;
2 3     3   50751 use strict;
  3         8  
  3         141  
3 3     3   17 use warnings FATAL => 'recursion';
  3         6  
  3         707  
4 3     3   73 use 5.008008;
  3         17  
  3         222  
5             our $VERSION = '0.10';
6 3     3   3735 use Term::ANSIColor;
  3         29772  
  3         268  
7 3     3   3734 use Data::Dumper;
  3         34009  
  3         1081  
8              
9             our $LEVEL;
10             our $BUFFER;
11              
12             sub new {
13 1     1 1 18 my $class = shift;
14 1         9 my %color = (
15             inserted_color => 'green',
16             deleted_color => 'red',
17             updated_color => 'blue',
18             comment_color => 'cyan',
19             );
20 1 50       6 if ($ENV{DD_COLOR}) {
21             # TYPE=FG;BG:TYPE=FG;BG:...
22 0         0 for my $type_color (split /:/, $ENV{DD_COLOR}) {
23 0         0 my($type, $color) = split /=/, $type_color, 2;
24 0         0 my($fg, $bg) = split /;/, $color, 2;
25 0         0 $type .= "_color";
26 0 0       0 $color{$type} = ($fg ? "$fg " : "").($bg ? "on_$bg" : "");
    0          
27             }
28             }
29             bless {
30 1         11 %color,
31             indent => 2,
32             }, $class;
33             }
34              
35 0     0   0 sub _f($) { die "Do not call directly"; }
36              
37             sub ddf {
38 67     67 0 609 my $self = shift;
39 67 50       134 @_==1 or die;
40              
41 67         76 local $Data::Dumper::Terse = 1;
42 67         66 local $Data::Dumper::Indent = 0;
43 67         144 Dumper(@_);
44             }
45              
46             sub compare {
47 8     8 1 25 my $self = shift;
48 8         15 local $LEVEL = 0;
49 8         12 local $BUFFER = '';
50 3     3   27 no warnings 'redefine';
  3         5  
  3         3434  
51 8     32   39 local *_f = sub($) { $self->ddf(@_) };
  32         443  
52 8         13 local $Term::ANSIColor::EACHLINE = "\n";
53 8         18 $self->_compare(@_);
54 8         345 return $BUFFER;
55             }
56              
57             # TODO: recursion detection
58             sub _compare {
59 11     11   20 my ($self, $a, $b) = @_;
60 11 100       34 if (ref $a eq 'HASH') { # dump hash
    100          
61 4 100       8 if (ref $b eq 'HASH') {
62 3         8 $self->_print("{\n");
63             {
64 3         149 local $LEVEL = $LEVEL + 1;
  3         7  
65 3         15 for my $key (sort keys %$a) {
66 8 100       240 if (exists $b->{$key}) {
67 5 100       16 if ($self->ddf($b->{$key}) eq $self->ddf($a->{$key})) {
68 1         38 $self->_print("%s => %s,\n", $self->ddf($key), $self->ddf($a->{$key}));
69             } else {
70 4 100 66     141 if (ref($a->{$key}) or ref($b->{$key})) {
71 1         3 $self->_print("%s => ", _f($key));
72 1         48 local $LEVEL = $LEVEL + 1;
73 1         18 $self->_compare($a->{$key}, $b->{$key});
74 1         35 $self->_print(",\n");
75             } else {
76 3         8 $self->_updated("%s => %s,", _f($key), _f($a->{$key}));
77 3         123 $self->_comment(" # != %s,\n", _f($b->{$key}));
78             }
79             }
80             } else {
81 3         7 $self->_inserted("%s => %s,\n", $self->ddf($key), $self->ddf($a->{$key}));
82             }
83             }
84 3         148 for my $key (sort keys %$b) {
85 8 100       65 next if exists $a->{$key};
86 3         9 $self->_deleted("%s => %s,\n", $self->ddf($key), $self->ddf($b->{$key}));
87             }
88             }
89 3         93 $self->_print("}\n");
90 3         120 return;
91             } else {
92 1         5 $self->_inserted("%s\n", $self->ddf($a));
93 1         79 $self->_deleted("%s\n", $self->ddf($b));
94             }
95             } elsif (ref $a eq 'ARRAY') {
96 5 100       14 if (ref $b eq 'ARRAY') {
97 4         7 $self->_print("[\n");
98             {
99 4         141 local $LEVEL = $LEVEL + 1;
  4         7  
100 4         6 my $alen = 0+@$a;
101 4         5 my $blen = 0+@$b;
102 4         5 my $i = 0;
103 4         3 while (1) {
104 14 100 100     62 if ($i<$alen && $i<$blen) { # both
    100          
    50          
105 8 100       13 if (_f($a->[$i]) eq _f($b->[$i])) {
106 4         126 $self->_print("%s,\n", _f($a->[$i]));
107             } else {
108 4 100 66     155 if (ref($a->[$i]) or ref($b->[$i])) {
109 2         3 local $LEVEL = $LEVEL + 1;
110 2         25 $self->_compare($a->[$i], $b->[$i]);
111             } else {
112 2         6 $self->_updated("%s,", $a->[$i]);
113 2         92 $self->_comment(" # != %s\n", $b->[$i]);
114             }
115             }
116             } elsif ($i<$alen) {
117 2         5 $self->_inserted("%s,\n", _f $a->[$i]);
118             } elsif ($i<$blen) {
119 0         0 $self->_deleted("%s,\n", _f $b->[$i]);
120             } else {
121 4         8 last;
122             }
123 10         352 ++$i;
124             }
125             }
126 4         10 $self->_print("]\n");
127             } else {
128 1         5 $self->_inserted("%s\n", $self->ddf($a));
129 1         53 $self->_deleted("%s\n", $self->ddf($b));
130             }
131             } else {
132 2 100       9 if ($self->ddf($a) eq $self->ddf($b)) {
133 1         38 $self->_print("%s\n", $self->ddf($a));
134             } else {
135 1         44 $self->_inserted("%s\n", $self->ddf($a));
136 1         51 $self->_deleted("%s\n", $self->ddf($b));
137             }
138             }
139             }
140              
141             sub _print {
142 22     22   241 my ($self, @args) = @_;
143 22         67 $BUFFER .= ' 'x($LEVEL*$self->{indent});
144 22         69 $BUFFER .= sprintf colored(['reset'], shift @args), @args;
145             }
146              
147             sub _inserted {
148 8     8   296 my ($self, @args) = @_;
149 8         27 $BUFFER .= ' 'x($LEVEL*$self->{indent});
150 8         27 $BUFFER .= sprintf colored([$self->{"inserted_color"}], shift @args), @args;
151             }
152              
153             sub _updated {
154 5     5   123 my ($self, @args) = @_;
155 5         14 $BUFFER .= ' 'x($LEVEL*$self->{indent});
156 5         32 $BUFFER .= sprintf colored([$self->{"updated_color"}], shift @args), @args;
157             }
158              
159             sub _deleted {
160 6     6   209 my ($self, @args) = @_;
161 6         14 $BUFFER .= ' 'x($LEVEL*$self->{indent});
162 6         20 $BUFFER .= sprintf colored([$self->{"deleted_color"}], shift @args), @args;
163             }
164              
165             sub _comment {
166 5     5   127 my ($self, @args) = @_;
167 5         33 $BUFFER .= ' 'x($LEVEL*$self->{indent});
168 5         24 $BUFFER .= sprintf colored([$self->{"comment_color"}], shift @args), @args;
169             }
170              
171             1;
172             __END__