package JSON::JQ;
use strict;
use warnings;
use Carp;
our $VERSION = '0.09';
# internal flags
our $DEBUG = 0;
our $DUMP_DISASM = 0;
use FindBin ();
FindBin::again();
use POSIX qw/isatty/;
use Path::Tiny qw/path/;
use JSON qw/from_json/;
# jv_print_flags in jv.h
use enum qw/BITMASK:JV_PRINT_ PRETTY ASCII COLOR SORTED INVALID REFCOUNT TAB ISATTY SPACE0 SPACE1 SPACE2/;
# jq.h
use enum qw/:JQ_DEBUG_=1 TRACE TRACE_DETAIL TRACE_ALL/;
use XSLoader;
XSLoader::load('JSON::JQ', $VERSION);
sub new {
my ( $pkg, $param ) = @_;
croak "script or script_file parameter required" unless exists $param->{script} or exists $param->{script_file};
my $self = {};
# script string or script file
$self->{script} = $param->{script} if exists $param->{script};
$self->{script_file} = $param->{script_file} if exists $param->{script_file};
# script initial arguments
$self->{variable} = exists $param->{variable} ? $param->{variable} : {};
# internal attributes
$self->{_attribute}->{JQ_ORIGIN} = path($FindBin::Bin)->realpath->stringify if $FindBin::Bin;
$self->{_attribute}->{JQ_LIBRARY_PATH} = exists $param->{library_paths} ? $param->{library_paths} :
[ '~/.jq', '$ORIGIN/../lib/jq', '$ORIGIN/lib' ];
$self->{_attribute}->{PROGRAM_ORIGIN} = exists $param->{script_file} ? path($param->{script_file})->parent->stringify : '.';
# error callback will push error messages into this array
$self->{_errors} = [];
# debug callback print flags
my $dump_opts = JV_PRINT_INDENT_FLAGS(2);
$dump_opts |= JV_PRINT_SORTED;
$dump_opts |= JV_PRINT_COLOR | JV_PRINT_ISATTY if isatty(*STDERR);
$self->{_dumpopts} = $dump_opts;
# jq debug flags
$self->{jq_flags} = exists $param->{debug_flag} ? $param->{debug_flag} : 0;
bless $self, $pkg;
unless ($self->_init()) {
croak "jq_compile_args() failed with errors:\n ". join("\n ", @{ $self->{_errors} });
}
return $self;
}
sub process {
my ( $self, $param ) = @_;
my $input;
if (exists $param->{data}) {
$input = $param->{data};
}
elsif (exists $param->{json}) {
$input = from_json($param->{json}, { utf8 => 1 });
}
elsif (exists $param->{json_file}) {
my $file = path($param->{json_file});
$input = from_json($file->slurp, { utf8 => 1 });
}
else {
croak "JSON::JQ::process(): required parameter not found, check method documentation";
}
my $output = [];
my $rc = $self->_process($input, $output);
# treat it as option EXIT_STATUS is on
$rc -= 10 if $rc >= 10;
if ($rc == 1) {
# NOTE: treat this case as successful run
warn "JSON::JQ::process(): returned null/false (undef output), perhaps the input is undef.\n";
}
elsif ($rc == 4 and @{ $self->{_errors} } == 0) {
# treat it as succeeded
push @$output, undef;
}
elsif ($rc != 0) {
croak "JSON::JQ::process(): failed with return code = $rc and errors:\n ". join("\n ", @{ $self->{_errors} });
}
return wantarray ? @$output : $output;
}
=head1 NAME
JSON::JQ - jq (https://stedolan.github.io/jq/) library binding
=head1 SYNOPSIS
use JSON::JQ;
my $jq = JSON::JQ->new({ script => '.' });
# 1. process perl data
my $results = $jq->process({ data => { foo => 'bar' }});
# 2. process json string
my $results = $jq->process({ json => '{ "foo": "bar" }'});
# 3. process json file
my $results = $jq->process({ json_file => 'foo.json' });
# check items in @$results
=head1 DESCRIPTION
This is a L library binding, making it possible to process
data using jq script/filter/module. Check the jq homepage for a detailed explanation and documentation.
=head1 METHODS
=head2 new({ parameter => value, ... })
Construct a jq engine instance and return it, jq script must be provided by either I