App-NDTools
view release on metacpan or search on metacpan
lib/App/NDTools/NDDiff.pm view on Meta::CPAN
package App::NDTools::NDDiff;
use strict;
use warnings FATAL => 'all';
use parent 'App::NDTools::NDTool';
use Algorithm::Diff qw(compact_diff);
use JSON qw();
use JSON::Patch 0.04 qw();
use App::NDTools::Slurp qw(s_dump);
use App::NDTools::Util qw(is_number);
use Log::Log4Cli 0.18;
use Struct::Diff 0.96 qw();
use Struct::Diff::MergePatch qw();
use Struct::Path 0.80 qw(path path_delta);
use Struct::Path::PerlStyle 0.80 qw(str2path path2str);
use Term::ANSIColor qw(color);
our $VERSION = '0.61';
my $JSON = JSON->new->canonical->allow_nonref;
my %COLOR;
sub arg_opts {
my $self = shift;
return (
$self->SUPER::arg_opts(),
'A!' => \$self->{OPTS}->{diff}->{A},
'N!' => \$self->{OPTS}->{diff}->{N},
'O!' => \$self->{OPTS}->{diff}->{O},
'R!' => \$self->{OPTS}->{diff}->{R},
'U!' => \$self->{OPTS}->{diff}->{U},
'brief' => sub { $self->{OPTS}->{ofmt} = $_[0] },
'colors!' => \$self->{OPTS}->{colors},
'ctx-text=i' => \$self->{OPTS}->{'ctx-text'},
'grep=s@' => \$self->{OPTS}->{grep},
'json' => sub { $self->{OPTS}->{ofmt} = $_[0] },
'ignore=s@' => \$self->{OPTS}->{ignore},
'rules' => sub { $self->{OPTS}->{ofmt} = $_[0] },
'quiet|q' => \$self->{OPTS}->{quiet},
'show' => \$self->{OPTS}->{show},
)
}
sub check_args {
my $self = shift;
if ($self->{OPTS}->{show}) {
die_fatal "At least one argument expected when --show used", 1
unless (@_);
} elsif (@_ < 2) {
die_fatal "At least two arguments expected for diff", 1;
}
return $self;
}
sub configure {
my $self = shift;
$self->SUPER::configure();
$self->{OPTS}->{colors} = $self->{TTY}
unless (defined $self->{OPTS}->{colors});
# resolve colors
while (my ($k, $v) = each %{$self->{OPTS}->{term}->{line}}) {
if ($self->{OPTS}->{colors}) {
$COLOR{$k} = color($v);
$COLOR{"B$k"} = color("bold $v");
} else {
$COLOR{$k} = $COLOR{"B$k"} = '';
}
}
$COLOR{head} = $self->{OPTS}->{colors}
? color($self->{OPTS}->{term}->{head}) : "";
$COLOR{reset} = $self->{OPTS}->{colors} ? color('reset') : "";
# resolve paths
for (@{$self->{OPTS}->{grep}}, @{$self->{OPTS}->{ignore}}) {
my $tmp = eval { str2path($_) };
die_fatal "Failed to parse '$_'", 4 if ($@);
$_ = $tmp;
}
$self->{OPTS}->{ofmt} = lc($self->{OPTS}->{ofmt});
# Use full diff (JSON Merge Patch does not provide arrays diffs)
map { $self->{OPTS}->{diff}->{$_} = 1 } keys %{$self->{OPTS}->{diff}},
if ($self->{OPTS}->{ofmt} eq 'jsonmergepatch');
return $self;
}
sub defaults {
my $self = shift;
my $out = {
%{$self->SUPER::defaults()},
'ctx-text' => 3,
'diff' => {
'A' => 1,
'N' => 1,
'O' => 1,
'R' => 1,
'U' => 0,
},
'ofmt' => 'term',
'term' => {
'head' => 'yellow',
'indt' => ' ',
'line' => {
'A' => 'green',
'D' => 'yellow',
'N' => 'green',
'O' => 'red',
'U' => 'white',
'R' => 'red',
'@' => 'magenta',
},
'sign' => {
'A' => '+ ',
'D' => '! ',
'N' => '+ ',
'O' => '- ',
'U' => ' ',
'R' => '- ',
'@' => ' ',
},
},
};
return $out;
}
sub diff {
my ($self, $old, $new) = @_;
log_debug { "Calculating diff for structure" };
my $diff = Struct::Diff::diff(
$old, $new,
map { ("no$_" => 1) } grep { !$self->{OPTS}->{diff}->{$_} }
keys %{$self->{OPTS}->{diff}},
);
# retrieve result from wrapper (see load() for more info)
if (exists $diff->{D}) {
$diff = $diff->{D}->[0];
} elsif (exists $diff->{U}) {
$diff->{U} = $diff->{U}->[0];
}
return $diff;
}
sub diff_term {
my ($self, $diff) = @_;
log_debug { "Calculating diffs for text values" };
( run in 0.887 second using v1.01-cache-2.11-cpan-8f98c5d2c55 )