package App::NDTools::NDProc::Module::Pipe;

use strict;
use warnings FATAL => 'all';
use parent 'App::NDTools::NDProc::Module';

use IPC::Run3;
use Log::Log4Cli;
use App::NDTools::Slurp qw(s_decode s_encode);
use Struct::Path 0.80 qw(path);

our $VERSION = '0.08';

sub MODINFO { "Modify structure using external process" }

sub arg_opts {
    my $self = shift;

    return (
        $self->SUPER::arg_opts(),
        'command|cmd=s' => \$self->{OPTS}->{command},
        'strict' => \$self->{OPTS}->{strict},
    )
}

sub check_rule {
    my ($self, $rule) = @_;
    my $out = $self;

    push @{$rule->{path}}, '' unless (@{$rule->{path}});

    unless (defined $rule->{command}) {
        log_error { 'Command to run should be defined' };
        $out = undef;
    }

    return $out;
}

sub process_path {
    my ($self, $data, $path, $spath, $opts) = @_;

    my @refs = eval { path(${$data}, $spath, strict => $opts->{strict}) };
    die_fatal "Failed to lookup path '$path'", 4 if ($@);

    for my $r (@refs) {
        my $in = s_encode(${$r}, 'JSON', { pretty => 1 });

        my ($out, $err);
        run3($opts->{command}, \$in, \$out, \$err, { return_if_system_error => 1});
        die_fatal "Failed to run '$opts->{command}' ($!)", 2
            if ($? == -1); # run3 specific
        unless ($? == 0) {
            die_fatal "'$opts->{command}' exited with " . ($? >> 8) .
                ($err ? " (" . join(" ", split("\n", $err)) . ")" : ""), 16;
        }

        ${$r} = s_decode($out, 'JSON');
    }
}

1; # End of App::NDTools::NDProc::Module::Pipe

__END__

=head1 NAME

Pipe - pass structure to external program and apply result.

=head1 OPTIONS

=over 4

=item B<--[no]blame>

Blame calculation toggle. Enabled by default.

=item B<--command|--cmd> E<lt>commandE<gt>

Command to run. JSON encoded structure passed to it's STDIN and it's STDOUT
applied to original structure. Exit 0 expected for success.

=item B<--cond> E<lt>pathE<gt>

Apply rule when condition met only. Condition is met when path leads to at
least one item in the structure. May be used several times (in this case
conditions are AND'ed).

=item B<--path> E<lt>pathE<gt>

Path to substructure to deal with.

=item B<--preserve> E<lt>pathE<gt>

Preserve specified substructure. May be used several times.

=item B<--strict>

Fail if specified path doesn't exist.

=back

=head1 SEE ALSO

L<ndproc>, L<ndproc-modules>

L<nddiff>, L<ndquery>, L<Struct::Path::PerlStyle>
