File Coverage

blib/lib/Value/Diff.pm
Criterion Covered Total %
statement 65 65 100.0
branch 39 40 97.5
condition 6 6 100.0
subroutine 12 12 100.0
pod 1 1 100.0
total 123 124 99.1


line stmt bran cond sub pod time code
1             package Value::Diff;
2             $Value::Diff::VERSION = '0.001';
3 7     7   479014 use v5.10;
  7         84  
4 7     7   40 use strict;
  7         13  
  7         157  
5 7     7   43 use warnings;
  7         23  
  7         264  
6              
7 7     7   66 use Exporter qw(import);
  7         29  
  7         295  
8 7     7   43 use Carp qw(croak);
  7         11  
  7         5163  
9              
10             our @EXPORT = qw(diff);
11              
12             my $no_diff = \'no_diff';
13              
14             sub _has_diff
15             {
16 141 100   141   260 return !!1 unless defined $_[0];
17 136         436 return $_[0] ne $no_diff;
18             }
19              
20             sub _diff_hash
21             {
22 20     20   36 my ($left, $right) = @_;
23              
24 20         34 my %out;
25 20         30 for my $key (keys %{$left}) {
  20         56  
26 33         55 my $value = $left->{$key};
27             $out{$key} = $value
28 33 100       77 unless exists $right->{$key};
29              
30 33         71 my $diff = _diff($value, $right->{$key});
31 33 100       61 $out{$key} = $diff
32             if _has_diff($diff);
33             }
34              
35 20 100       61 return %out ? \%out : $no_diff;
36             }
37              
38             sub _diff_array
39             {
40 22     22   40 my ($left, $right) = @_;
41              
42 22         29 my @out;
43 22         28 my @other = @{$right};
  22         55  
44              
45             OUTER:
46 22         34 for my $value (@{$left}) {
  22         37  
47 38         75 for my $key (0 .. $#other) {
48 46         66 my $other_value = $other[$key];
49 46 100       74 if (!_has_diff(_diff($value, $other_value))) {
50 27         45 splice @other, $key, 1;
51 27         65 next OUTER;
52             }
53             }
54              
55             # TODO: take the smallest diff instead of full $left?
56 11         23 push @out, $value;
57             }
58              
59 22 100       68 return @out ? \@out : $no_diff;
60             }
61              
62             sub _diff_scalar
63             {
64 13     13   23 my ($left, $right) = @_;
65              
66 13         50 my $diff = _diff($$left, $$right);
67 13 100       29 return _has_diff($diff) ? \$diff : $no_diff;
68             }
69              
70             sub _diff
71             {
72 141     141   251 my ($left, $right) = @_;
73              
74 141         253 my $ref_left = ref $left;
75 141         181 my $ref_right = ref $right;
76 141 100       283 return $left if $ref_left ne $ref_right;
77 134 100       274 return _diff_array($left, $right) if $ref_left eq 'ARRAY';
78 112 100       216 return _diff_hash($left, $right) if $ref_left eq 'HASH';
79 92 100       202 return _diff_scalar($left, $right) if $ref_left eq 'SCALAR';
80 83 100       176 return _diff_scalar($left, $right) if $ref_left eq 'REF';
81              
82 79 50       136 croak "cannot compare references to $ref_left"
83             if $ref_left ne '';
84              
85 79 100 100     415 return $left
      100        
86             if defined $left ne defined $right
87             || (defined $left && $left ne $right);
88              
89 55         102 return $no_diff;
90             }
91              
92             sub _empty_of_type
93             {
94 5     5   11 my ($left) = @_;
95              
96 5         8 my $type = ref $left;
97 5 100       11 return [] if $type eq 'ARRAY';
98 4 100       10 return {} if $type eq 'HASH';
99 3 100       6 return \undef if $type eq 'SCALAR';
100 2         5 return undef;
101             }
102              
103             sub diff
104             {
105 49     49 1 28703 my ($left, $right, $out) = @_;
106              
107 49         120 my $diff = _diff($left, $right);
108              
109 49 100       115 if (_has_diff($diff)) {
110 23 100       83 $$out = $diff if ref $out;
111 23         139 return !!1;
112             }
113             else {
114 26 100       65 $$out = _empty_of_type($left) if ref $out;
115 26         140 return !!0;
116             }
117             }
118              
119             1;
120              
121             # ABSTRACT: find out the difference between two Perl values
122