File Coverage

blib/lib/App/diffdir.pm
Criterion Covered Total %
statement 72 96 75.0
branch 24 44 54.5
condition 3 12 25.0
subroutine 11 14 78.5
pod 0 7 0.0
total 110 173 63.5


line stmt bran cond sub pod time code
1             package App::diffdir;
2              
3             # Created on: 2015-03-05 19:52:53
4             # Create by: Ivan Wills
5             # $Id$
6             # $Revision$, $HeadURL$, $Date$
7             # $Revision$, $Source$, $Date$
8              
9 3     3   58657 use Moo;
  3         27534  
  3         13  
10 3     3   3519 use strict;
  3         6  
  3         46  
11 3     3   10 use warnings;
  3         7  
  3         70  
12 3     3   545 use Data::Dumper qw/Dumper/;
  3         5485  
  3         130  
13 3     3   495 use English qw/ -no_match_vars /;
  3         2915  
  3         20  
14 3     3   1551 use Path::Tiny;
  3         10538  
  3         136  
15 3     3   1625 use Text::Diff;
  3         20990  
  3         3003  
16              
17             our $VERSION = 0.9;
18              
19             has files => (
20             is => 'rw',
21             default => sub {{}},
22             );
23             has exclude => (
24             is => 'rw',
25             default => sub {[]},
26             );
27             has cmd => (
28             is => 'rw',
29             default => 'diff',
30             );
31             has [qw/
32             fast
33             follow
34             ignore_all_space
35             ignore_space_change
36             verbose
37             /] => (
38             is => 'rw',
39             );
40              
41             sub differences {
42 1     1 0 3768 my ($self, @dirs) = @_;
43              
44 1         3 my %found = $self->get_files(@dirs);
45              
46 1         4 for my $file (keys %found) {
47 6         8 my $last_dir = $dirs[0];
48 6         6 my $diff_count = 0;
49              
50 6 100       13 if ( ! $found{$file}{$last_dir} ) {
51 1         3 $found{$file}{$last_dir} = {
52             name => path( $last_dir, $file),
53             diff => 'missing',
54             };
55             }
56              
57 6         39 for my $dir (@dirs[1 .. @dirs - 1]) {
58 6 100       24 if ( ! $found{$file}{$dir} ) {
    100          
    100          
59 1         2 $found{$file}{$dir} = {
60             name => path( $dir, $file),
61             diff => 'missing',
62             };
63 1         27 $diff_count++;
64             }
65             elsif ( ! -e $found{$file}{$last_dir}{name} ) {
66 1         20 $found{$file}{$dir}{diff} = 'added';
67 1         2 $diff_count++;
68             }
69 4         68 elsif ( my $diff = eval { $self->dodiff( ''.path($last_dir, $file), ''.path($dir, $file) ) } ) {
70 1         3 $found{$file}{$dir}{diff} = $diff;
71 1         2 $diff_count++;
72             }
73 6 50       16 warn $@ if $@;
74 6         10 $last_dir = $dir;
75             }
76              
77 6 100       12 if ( !$diff_count ) {
78 3         10 delete $found{$file};
79             }
80             }
81              
82 1         5 return %found;
83             }
84              
85             sub get_files {
86 2     2 0 4124 my ($self, @dirs) = @_;
87 2         4 my %found;
88              
89 2         3 for my $dir (@dirs) {
90 4         15 $dir =~ s{/$}{};
91 4         8 my @found = $self->find_files($dir);
92 4         8 for my $file (@found) {
93 20         27 my $base = $file;
94 20 50       28 if ( $dir ne '.' ) {
95             # remove the base directory from the file name
96 20         102 $base =~ s/^\Q$dir\E\/?//;
97             }
98 20         167 $found{$base}{$dir} = {
99             name => $file,
100             };
101             }
102             }
103              
104 2         13 return %found;
105             }
106              
107             sub find_files {
108 6     6 0 3860 my ($self, $dir) = @_;
109 6         18 my @files = path($dir)->children;
110 6         1313 my @found;
111              
112             FILE:
113 6         40 while ( my $file = shift @files ) {
114 34 100       1186 next FILE if $file->basename =~ /^[.].*[.]sw[n-z]$|^[.](?:svn|bzr|git)$|CVS|RCS$|cover_db|_build|Build$|blib/;
115 28 100 66     694 next FILE if $self->{exclude} && grep {$file =~ /$_/} @{ $self->{exclude} };
  4         29  
  28         72  
116              
117 27         60 push @found, $file;
118              
119 27 100       62 if ( -d $file ) {
120 6         87 push @files, $file->children;
121             }
122             }
123              
124 6         119 return @found;
125             }
126              
127             my $which_diff;
128             sub dodiff {
129 4     4 0 207 my ($self, $file1, $file2) = @_;
130              
131 4 100       8 if ( ! $which_diff ) {
132 1 50 33     8 $which_diff = $self->ignore_space_change || $self->ignore_all_space
133             ? 'mydiff'
134             : 'text';
135             }
136              
137 4 50       7 if ( $which_diff eq 'mydiff' ) {
138 0         0 return $self->mydiff($file1, $file2);
139             }
140             else {
141 4         9 my $diff = diff($file1, $file2);
142 4 100       1278 return (length $diff, $self->cmd . " $file1 $file2") if $diff;
143             }
144              
145 3         9 return;
146             }
147              
148             sub mydiff {
149 0     0 0   my ($self, $file1, $file2) = @_;
150              
151 0 0 0       return if !$self->follow && (-l $file1 || -l $file2);
      0        
152              
153 0           my $file1_q = shell_quote($file1);
154 0           my $file2_q = shell_quote($file2);
155              
156 0           my $cmd = '/usr/bin/diff';
157 0 0         if ( $self->ignore_space_change ) {
158 0           $cmd .= ' --ignore-space-change';
159             }
160 0 0         if ( $self->ignore_all_space ) {
161 0           $cmd .= ' --ignore-all-space';
162             }
163 0           $cmd .= " $file1_q $file2_q";
164 0 0         my $diff
    0          
165             = -s $file1 != -s $file2 ? abs( (-s $file1) - (-s $file2) )
166             : $self->fast ? 0
167             : length ''.`$cmd`;
168              
169 0 0         if ($diff) {
170 0 0         warn "$self->cmd $file1_q $file2_q\n" if $self->verbose;
171 0           return ( $diff, "$self->cmd $file1_q $file2_q" );
172             }
173              
174 0           return;
175             }
176              
177             sub shell_quote {
178 0     0 0   my ($text) = @_;
179              
180 0 0         if ($text =~ /[\s$|><;#]/xms) {
181 0           $text =~ s/'/'\\''/gxms;
182 0           $text = "'$text'";
183             }
184              
185 0           return $text;
186             }
187              
188             sub basename {
189 0     0 0   my ($self, $dir, $file) = @_;
190 0           $file =~ s{^$dir/?}{};
191 0           return $file;
192             }
193              
194             1;
195              
196             __END__