#!perl

### code_after_shebang
# Note: This script is a CLI for Riap function /App/podtohtml/podtohtml
# and generated automatically using Perinci::CmdLine::Gen version 0.486

# PERICMD_INLINE_SCRIPT: {"code_after_shebang":"...","config_dirs":null,"config_filename":"podtohtml.conf","env_name":"PODTOHTML_OPT","include":null,"log":null,"pack_deps":1,"pod":0,"read_config":"0","read_env":"0","script_name":"podtohtml","script_summary":null,"script_version":"0.004","shebang":"perl","skip_format":0,"subcommands":null,"url":"/App/podtohtml/podtohtml","use_cleanser":1,"validate_args":1}

my $_pci_metas = {""=>{args=>{browser=>{cmdline_aliases=>{b=>{}},schema=>["bool",{is=>1,req=>1},{}],summary=>"Instead of outputing HTML to STDOUT/file, view it in browser"},infile=>{cmdline_aliases=>{i=>{}},default=>"-",description=>"\nIf not found, will search in for .pod or .pm files in `\@INC`.\n\n",pos=>0,schema=>["perl::pod_or_pm_filename",{req=>1},{}],summary=>"Input file (POD)"},outfile=>{cmdline_aliases=>{o=>{}},pos=>1,schema=>["filename",{req=>1},{}]}},args_rels=>{choose_one=>["outfile","browser"]},"cmdline.skip_format"=>1,description=>"\nThis is a thin wrapper for <pm:Pod::Html> and an alternative CLI to\n<prog:pod2html> to remove some annoyances that I experience with `pod2html`,\ne.g. the default cache directory being `.` (so it leaves `.tmp` files around).\nThis CLI also offers tab completion.\n\nIt does not yet offer as many options as `pod2html`.\n\n",examples=>[{argv=>["some.pod"],summary=>"Convert POD file to HTML, print result to STDOUT",test=>0,"x.doc.show_result"=>0},{argv=>["some.pod","-b"],summary=>"Convert POD file to HTML, show result in browser",test=>0,"x.doc.show_result"=>0}],result=>{},summary=>"Convert POD to HTML",v=>1.1}};

# This script is generated by Perinci::CmdLine::Inline version 0.543 on Tue Sep 11 17:36:49 2018.

# Rinci metadata taken from these modules: App::podtohtml (no version)

# You probably should not manually edit this file.

our $DATE = '2018-09-11'; # DATE
our $VERSION = '0.004'; # VERSION
# PODNAME: podtohtml
# ABSTRACT: Convert POD to HTML

# BEGIN DATAPACK CODE
{
    my $toc;
    my $data_linepos = 1;
    unshift @INC, sub {
        $toc ||= do {

            my $fh = \*DATA;

        my $header_line;
        my $header_found;
        while (1) {
            my $header_line = <$fh>;
            defined($header_line)
                or die "Unexpected end of data section while reading header line";
            chomp($header_line);
            if ($header_line eq 'Data::Section::Seekable v1') {
                $header_found++;
                last;
            }
        }
        die "Can't find header 'Data::Section::Seekable v1'"
            unless $header_found;

        my %toc;
        my $i = 0;
        while (1) {
            $i++;
            my $toc_line = <$fh>;
            defined($toc_line)
                or die "Unexpected end of data section while reading TOC line #$i";
            chomp($toc_line);
            $toc_line =~ /\S/ or last;
            $toc_line =~ /^([^,]+),(\d+),(\d+)(?:,(.*))?$/
                or die "Invalid TOC line #$i in data section: $toc_line";
            $toc{$1} = [$2, $3, $4];
        }
        my $pos = tell $fh;
        $toc{$_}[0] += $pos for keys %toc;


            # calculate the line number of data section
            my $data_pos = tell(DATA);
            seek DATA, 0, 0;
            my $pos = 0;
            while (1) {
                my $line = <DATA>;
                $pos += length($line);
                $data_linepos++;
                last if $pos >= $data_pos;
            }
            seek DATA, $data_pos, 0;

            \%toc;
        };
        if ($toc->{$_[1]}) {
            seek DATA, $toc->{$_[1]}[0], 0;
            read DATA, my($content), $toc->{$_[1]}[1];
            my ($order, $lineoffset) = split(';', $toc->{$_[1]}[2]);
            $content =~ s/^#//gm;
            $content = "# line ".($data_linepos + $order+1 + $lineoffset)." \"".__FILE__."\"\n" . $content;
            open my $fh, '<', \$content
                or die "DataPacker error loading $_[1]: $!";
            return $fh;
        }
        return;
    };
}
# END DATAPACK CODE

package main;
use 5.010001;
use strict;
#use warnings;

# modules


### declare global variables

our $_pci_meta_result_stream = 0;
our $_pci_meta_result_type;
our $_pci_meta_result_type_is_simple;
our $_pci_meta_skip_format = 0;
our $_pci_r = {naked_res=>0,read_config=>0,read_env=>0,subcommand_name=>""};
our %_pci_args;

### declare subroutines

sub _pci_err {
    my $res = shift;
    print STDERR "ERROR $res->[0]: $res->[1]\n";
    exit $res->[0]-300;
}

sub _pci_json {
    state $json = do {
        if (eval { require JSON::XS; 1 }) { JSON::XS->new->canonical(1)->allow_nonref }
        else { require JSON::PP; JSON::PP->new->canonical(1)->allow_nonref }
    };
    $json;
}

### get arguments (from config file, env, command-line args

{
my %mentioned_args;
require Getopt::Long::EvenLess;
my $go_spec1 = {
    'format=s' => sub { $_pci_r->{format} = $_[1]; },
    'help|h|?' => sub { print "podtohtml - Convert POD to HTML\n\nUsage:\n  podtohtml --help (or -h, -?)\n  podtohtml --version (or -v)\n  podtohtml [options] [infile] [outfile]\n\nExamples:\n  Convert POD file to HTML, print result to STDOUT:\n  % podtohtml some.pod\n\n  Convert POD file to HTML, show result in browser:\n  % podtohtml some.pod -b\n\nThis is a thin wrapper for <pm:Pod::Html> and an alternative CLI to\n<prog:pod2html> to remove some annoyances that I experience with `pod2html`,\ne.g. the default cache directory being `.` (so it leaves `.tmp` files around).\nThis CLI also offers tab completion.\n\nIt does not yet offer as many options as `pod2html`.\n\nMain options:\n  --browser, -b    Instead of outputing HTML to STDOUT/file, view it in browser\n  --infile=s, -i   Input file (POD) (=arg[0]) [\"-\"]\n  --outfile=s, -o   (=arg[1])\n\nOutput options:\n  --format=s  Choose output format, e.g. json, text\n  --json      Set output format to json\n\nOther options:\n  --help, -h, -?                 Display help message and exit\n  --naked-res                    When outputing as JSON, strip result envelope\n  --no-naked-res, --nonaked-res  When outputing as JSON, don't strip result envelope\n  --version, -v                  Display program's version and exit\n"; exit 0; },
    'json' => sub { $_pci_r->{format} = (-t STDOUT) ? "json-pretty" : "json"; },
    'naked-res' => sub { $_pci_r->{naked_res} = 1; },
    'no-naked-res|nonaked-res' => sub { $_pci_r->{naked_res} = 0; },
    'version|v' => sub { no warnings 'once'; require App::podtohtml; print "podtohtml version ", "0.004", ($App::podtohtml::DATE ? " ($App::podtohtml::DATE)" : ''), "\n"; print "  Generated by Perinci::CmdLine::Inline version 0.543 (2018-09-11)\n"; exit 0 },
};
my $go_spec2 = {
    'b' => sub {         $_pci_args{'browser'} = $_[1];
 },
    'browser' => sub {         $_pci_args{'browser'} = $_[1];
 },
    'format=s' => sub {  },
    'help|h|?' => sub {  },
    'i=s' => sub {         $_pci_args{'infile'} = $_[1];
 },
    'infile=s' => sub {         $_pci_args{'infile'} = $_[1];
 },
    'json' => sub {  },
    'naked-res' => sub {  },
    'no-naked-res|nonaked-res' => sub {  },
    'o=s' => sub {         $_pci_args{'outfile'} = $_[1];
 },
    'outfile=s' => sub {         $_pci_args{'outfile'} = $_[1];
 },
    'version|v' => sub {  },
};
my $old_conf = Getopt::Long::EvenLess::Configure("pass_through");
Getopt::Long::EvenLess::GetOptions(%$go_spec1);
Getopt::Long::EvenLess::Configure($old_conf);
my $res = Getopt::Long::EvenLess::GetOptions(%$go_spec2);
_pci_err([500, "GetOptions failed"]) unless $res;
}

### check arguments

{
require Local::_pci_check_args; my $res = _pci_check_args(\%_pci_args);
_pci_err($res) if $res->[0] != 200;
$_pci_r->{args} = \%_pci_args;
}

### call function

{
my $sc_name = $_pci_r->{subcommand_name};
if ($sc_name eq "") {
    $_pci_meta_skip_format = 1;
    $_pci_meta_result_type = "";
    require App::podtohtml;
    eval { $_pci_r->{res} = App::podtohtml::podtohtml(%_pci_args) };
    if ($@) { $_pci_r->{res} = [500, "Function died: $@"] }
}
}

### format & display result

{
my $fres;
my $save_res; if (exists $_pci_r->{res}[3]{"cmdline.result"}) { $save_res = $_pci_r->{res}[2]; $_pci_r->{res}[2] = $_pci_r->{res}[3]{"cmdline.result"} }
my $is_success = $_pci_r->{res}[0] =~ /\A2/ || $_pci_r->{res}[0] == 304;
my $is_stream = $_pci_r->{res}[3]{stream} // $_pci_meta_result_stream // 0;
if ($is_success && (0 || $_pci_meta_skip_format || $_pci_r->{res}[3]{"cmdline.skip_format"})) { $fres = $_pci_r->{res}[2] }
elsif ($is_success && $is_stream) {}
else { require Local::_pci_clean_json; require Perinci::Result::Format::Lite; $is_stream=0; _pci_clean_json($_pci_r->{res}); $fres = Perinci::Result::Format::Lite::format($_pci_r->{res}, ($_pci_r->{format} // $_pci_r->{res}[3]{"cmdline.default_format"} // "text"), $_pci_r->{naked_res}, 0) }

my $use_utf8 = $_pci_r->{res}[3]{"x.hint.result_binary"} ? 0 : 0;
if ($use_utf8) { binmode STDOUT, ":encoding(utf8)" }
if ($is_stream) {
    my $code = $_pci_r->{res}[2]; if (ref($code) ne "CODE") { die "Result is a stream but no coderef provided" } if ($_pci_meta_result_type_is_simple) { while(defined(my $l=$code->())) { print $l; print "\n" unless $_pci_meta_result_type eq "buf"; } } else { while (defined(my $rec=$code->())) { print _pci_json()->encode($rec),"\n" } }
} else {
    print $fres;
}
if (defined $save_res) { $_pci_r->{res}[2] = $save_res }
}

### exit

{
my $status = $_pci_r->{res}[0];
my $exit_code = $_pci_r->{res}[3]{"cmdline.exit_code"} // ($status =~ /200|304/ ? 0 : ($status-300));
exit($exit_code);
}

=pod

=encoding UTF-8

=head1 NAME

podtohtml - Convert POD to HTML

=head1 VERSION

This document describes version 0.004 of main (from Perl distribution App-podtohtml), released on 2018-09-11.

=head1 SYNOPSIS

Usage:

 % podtohtml [options] [infile] [outfile]

Examples:

Convert POD file to HTML, print result to STDOUT:

 % podtohtml some.pod

Convert POD file to HTML, show result in browser:

 % podtohtml some.pod -b

=head1 DESCRIPTION

This is a thin wrapper for L<Pod::Html> and an alternative CLI to
L<pod2html> to remove some annoyances that I experience with C<pod2html>,
e.g. the default cache directory being C<.> (so it leaves C<.tmp> files around).
This CLI also offers tab completion.

It does not yet offer as many options as C<pod2html>.

=head1 OPTIONS

C<*> marks required options.

=head2 Main options

=over

=item B<--browser>, B<-b>

Instead of outputing HTML to STDOUT/file, view it in browser.

=item B<--infile>=I<s>, B<-i>

Input file (POD).

Default value:

 "-"

If not found, will search in for .pod or .pm files in `@INC`.


=item B<--outfile>=I<s>, B<-o>

=back

=head2 Output options

=over

=item B<--format>=I<s>

Choose output format, e.g. json, text.

Default value:

 undef

=item B<--json>

Set output format to json.

=item B<--naked-res>

When outputing as JSON, strip result envelope.

Default value:

 0

By default, when outputing as JSON, the full enveloped result is returned, e.g.:

    [200,"OK",[1,2,3],{"func.extra"=>4}]

The reason is so you can get the status (1st element), status message (2nd
element) as well as result metadata/extra result (4th element) instead of just
the result (3rd element). However, sometimes you want just the result, e.g. when
you want to pipe the result for more post-processing. In this case you can use
`--naked-res` so you just get:

    [1,2,3]


=back

=head2 Other options

=over

=item B<--help>, B<-h>, B<-?>

Display help message and exit.

=item B<--version>, B<-v>

Display program's version and exit.

=back

=head1 COMPLETION

The script comes with a companion shell completer script (L<_podtohtml>)
for this script.

=head2 bash

To activate bash completion for this script, put:

 complete -C _podtohtml podtohtml

in your bash startup (e.g. F<~/.bashrc>). Your next shell session will then
recognize tab completion for the command. Or, you can also directly execute the
line above in your shell to activate immediately.

It is recommended, however, that you install modules using L<cpanm-shcompgen>
which can activate shell completion for scripts immediately.

=head2 tcsh

To activate tcsh completion for this script, put:

 complete podtohtml 'p/*/`podtohtml`/'

in your tcsh startup (e.g. F<~/.tcshrc>). Your next shell session will then
recognize tab completion for the command. Or, you can also directly execute the
line above in your shell to activate immediately.

It is also recommended to install L<shcompgen> (see above).

=head2 other shells

For fish and zsh, install L<shcompgen> as described above.

=head1 HOMEPAGE

Please visit the project's homepage at L<https://metacpan.org/release/App-podtohtml>.

=head1 SOURCE

Source repository is at L<https://github.com/perlancar/perl-App-podtohtml>.

=head1 BUGS

Please report any bugs or feature requests on the bugtracker website L<https://rt.cpan.org/Public/Dist/Display.html?Name=App-podtohtml>

When submitting a bug or request, please include a test-file or a
patch to an existing test-file that illustrates the bug or desired
feature.

=head1 AUTHOR

perlancar <perlancar@cpan.org>

=head1 COPYRIGHT AND LICENSE

This software is copyright (c) 2018, 2017 by perlancar@cpan.org.

This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.

=cut

__DATA__
Data::Section::Seekable v1
Clone/PP.pm,20,6331,0;0
Data/Check/Structure.pm,6383,8606,1;193
Getopt/Long/EvenLess.pm,15021,12600,2;529
Local/_pci_check_args.pm,27654,3867,3;929
Local/_pci_clean_json.pm,31554,3724,4;1005
Perinci/Result/Format/Lite.pm,35316,22687,5;1058
Text/Table/Tiny.pm,58030,7678,6;1656

### Clone/PP.pm ###
#package Clone::PP;
#
#use 5.006;
#use strict;
#use warnings;
#use vars qw($VERSION @EXPORT_OK);
#use Exporter;
#
#$VERSION = 1.07;
#
#@EXPORT_OK = qw( clone );
#sub import { goto &Exporter::import } # lazy Exporter
#
## These methods can be temporarily overridden to work with a given class.
#use vars qw( $CloneSelfMethod $CloneInitMethod );
#$CloneSelfMethod ||= 'clone_self';
#$CloneInitMethod ||= 'clone_init';
#
## Used to detect looped networks and avoid infinite recursion. 
#use vars qw( %CloneCache );
#
## Generic cloning function
#sub clone {
#  my $source = shift;
#
#  return undef if not defined($source);
#  
#  # Optional depth limit: after a given number of levels, do shallow copy.
#  my $depth = shift;
#  return $source if ( defined $depth and $depth -- < 1 );
#  
#  # Maintain a shared cache during recursive calls, then clear it at the end.
#  local %CloneCache = ( undef => undef ) unless ( exists $CloneCache{undef} );
#  
#  return $CloneCache{ $source } if ( defined $CloneCache{ $source } );
#  
#  # Non-reference values are copied shallowly
#  my $ref_type = ref $source or return $source;
#  
#  # Extract both the structure type and the class name of referent
#  my $class_name;
#  if ( "$source" =~ /^\Q$ref_type\E\=([A-Z]+)\(0x[0-9a-f]+\)$/ ) {
#    $class_name = $ref_type;
#    $ref_type = $1;
#    # Some objects would prefer to clone themselves; check for clone_self().
#    return $CloneCache{ $source } = $source->$CloneSelfMethod() 
#				  if $source->can($CloneSelfMethod);
#  }
#  
#  # To make a copy:
#  # - Prepare a reference to the same type of structure;
#  # - Store it in the cache, to avoid looping if it refers to itself;
#  # - Tie in to the same class as the original, if it was tied;
#  # - Assign a value to the reference by cloning each item in the original;
#  
#  my $copy;
#  if ($ref_type eq 'HASH') {
#    $CloneCache{ $source } = $copy = {};
#    if ( my $tied = tied( %$source ) ) { tie %$copy, ref $tied }
#    %$copy = map { ! ref($_) ? $_ : clone($_, $depth) } %$source;
#  } elsif ($ref_type eq 'ARRAY') {
#    $CloneCache{ $source } = $copy = [];
#    if ( my $tied = tied( @$source ) ) { tie @$copy, ref $tied }
#    @$copy = map { ! ref($_) ? $_ : clone($_, $depth) } @$source;
#  } elsif ($ref_type eq 'REF' or $ref_type eq 'SCALAR') {
#    $CloneCache{ $source } = $copy = \( my $var = "" );
#    if ( my $tied = tied( $$source ) ) { tie $$copy, ref $tied }
#    $$copy = clone($$source, $depth);
#  } else {
#    # Shallow copy anything else; this handles a reference to code, glob, regex
#    $CloneCache{ $source } = $copy = $source;
#  }
#  
#  # - Bless it into the same class as the original, if it was blessed;
#  # - If it has a post-cloning initialization method, call it.
#  if ( $class_name ) {
#    bless $copy, $class_name;
#    $copy->$CloneInitMethod() if $copy->can($CloneInitMethod);
#  }
#  
#  return $copy;
#}
#
#1;
#
#__END__
#
#=head1 NAME
#
#Clone::PP - Recursively copy Perl datatypes
#
#=head1 SYNOPSIS
#
#  use Clone::PP qw(clone);
#  
#  $item = { 'foo' => 'bar', 'move' => [ 'zig', 'zag' ]  };
#  $copy = clone( $item );
#
#  $item = [ 'alpha', 'beta', { 'gamma' => 'vlissides' } ];
#  $copy = clone( $item );
#
#  $item = Foo->new();
#  $copy = clone( $item );
#
#Or as an object method:
#
#  require Clone::PP;
#  push @Foo::ISA, 'Clone::PP';
#  
#  $item = Foo->new();
#  $copy = $item->clone();
#
#=head1 DESCRIPTION
#
#This module provides a general-purpose clone function to make deep
#copies of Perl data structures. It calls itself recursively to copy
#nested hash, array, scalar and reference types, including tied
#variables and objects.
#
#The clone() function takes a scalar argument to copy. To duplicate
#arrays or hashes, pass them in by reference:
#
#  my $copy = clone(\@array);    my @copy = @{ clone(\@array) };
#  my $copy = clone(\%hash);     my %copy = %{ clone(\%hash) };
#
#The clone() function also accepts an optional second parameter that
#can be used to limit the depth of the copy. If you pass a limit of
#0, clone will return the same value you supplied; for a limit of
#1, a shallow copy is constructed; for a limit of 2, two layers of
#copying are done, and so on.
#
#  my $shallow_copy = clone( $item, 1 );
#
#To allow objects to intervene in the way they are copied, the
#clone() function checks for a couple of optional methods. If an
#object provides a method named C<clone_self>, it is called and the
#result returned without further processing. Alternately, if an
#object provides a method named C<clone_init>, it is called on the
#copied object before it is returned.
#
#=head1 BUGS
#
#Some data types, such as globs, regexes, and code refs, are always copied shallowly.
#
#References to hash elements are not properly duplicated. (This is why two tests in t/dclone.t that are marked "todo".) For example, the following test should succeed but does not:
#
#  my $hash = { foo => 1 }; 
#  $hash->{bar} = \{ $hash->{foo} }; 
#  my $copy = clone( \%hash ); 
#  $hash->{foo} = 2; 
#  $copy->{foo} = 2; 
#  ok( $hash->{bar} == $copy->{bar} );
#
#To report bugs via the CPAN web tracking system, go to 
#C<http://rt.cpan.org/NoAuth/Bugs.html?Dist=Clone-PP> or send mail 
#to C<Dist=Clone-PP#rt.cpan.org>, replacing C<#> with C<@>.
#
#=head1 SEE ALSO
#
#L<Clone> - a baseclass which provides a C<clone()> method.
#
#L<MooseX::Clone> - find-grained cloning for Moose objects.
#
#The C<dclone()> function in L<Storable>.
#
#L<Data::Clone> -
#polymorphic data cloning (see its documentation for what that means).
#
#L<Clone::Any> - use whichever of the cloning methods is available.
#
#=head1 REPOSITORY
#
#L<https://github.com/neilbowers/Clone-PP>
#
#=head1 AUTHOR AND CREDITS
#
#Developed by Matthew Simon Cavalletto at Evolution Softworks. 
#More free Perl software is available at C<www.evoscript.org>.
#
#
#=head1 COPYRIGHT AND LICENSE
#
#Copyright 2003 Matthew Simon Cavalletto. You may contact the author
#directly at C<evo@cpan.org> or C<simonm@cavalletto.org>.
#
#Code initially derived from Ref.pm. Portions Copyright 1994 David Muir Sharnoff.
#
#Interface based by Clone by Ray Finch with contributions from chocolateboy.
#Portions Copyright 2001 Ray Finch. Portions Copyright 2001 chocolateboy. 
#
#You may use, modify, and distribute this software under the same terms as Perl.
#
#=cut
### Data/Check/Structure.pm ###
#package Data::Check::Structure;
#
#our $DATE = '2017-07-18'; # DATE
#our $VERSION = '0.04'; # VERSION
#
#use strict;
##use warnings;
#
#use Exporter 'import';
#our @EXPORT_OK = qw(
#                       is_aoa
#                       is_aoaos
#                       is_aoh
#                       is_aohos
#                       is_aos
#                       is_hoa
#                       is_hoaos
#                       is_hoh
#                       is_hohos
#                       is_hos
#               );
#
#sub is_aos {
#    my ($data, $opts) = @_;
#    $opts ||= {};
#    my $max = $opts->{max};
#
#    return 0 unless ref($data) eq 'ARRAY';
#    for my $i (0..@$data-1) {
#        last if defined($max) && $i >= $max;
#        return 0 if ref($data->[$i]);
#    }
#    1;
#}
#
#sub is_aoa {
#    my ($data, $opts) = @_;
#    $opts ||= {};
#    my $max = $opts->{max};
#
#    return 0 unless ref($data) eq 'ARRAY';
#    for my $i (0..@$data-1) {
#        last if defined($max) && $i >= $max;
#        return 0 unless ref($data->[$i]) eq 'ARRAY';
#    }
#    1;
#}
#
#sub is_aoaos {
#    my ($data, $opts) = @_;
#    $opts ||= {};
#    my $max = $opts->{max};
#
#    return 0 unless ref($data) eq 'ARRAY';
#    my $aos_opts = {max=>$max};
#    for my $i (0..@$data-1) {
#        last if defined($max) && $i >= $max;
#        return 0 unless is_aos($data->[$i], $aos_opts);
#    }
#    1;
#}
#
#sub is_aoh {
#    my ($data, $opts) = @_;
#    $opts ||= {};
#    my $max = $opts->{max};
#
#    return 0 unless ref($data) eq 'ARRAY';
#    for my $i (0..@$data-1) {
#        last if defined($max) && $i >= $max;
#        return 0 unless ref($data->[$i]) eq 'HASH';
#    }
#    1;
#}
#
#sub is_aohos {
#    my ($data, $opts) = @_;
#    $opts ||= {};
#    my $max = $opts->{max};
#
#    return 0 unless ref($data) eq 'ARRAY';
#    my $hos_opts = {max=>$max};
#    for my $i (0..@$data-1) {
#        last if defined($max) && $i >= $max;
#        return 0 unless is_hos($data->[$i], $hos_opts);
#    }
#    1;
#}
#
#sub is_hos {
#    my ($data, $opts) = @_;
#    $opts ||= {};
#    my $max = $opts->{max};
#
#    return 0 unless ref($data) eq 'HASH';
#    my $i = 0;
#    for my $k (keys %$data) {
#        last if defined($max) && ++$i >= $max;
#        return 0 if ref($data->{$k});
#    }
#    1;
#}
#
#sub is_hoa {
#    my ($data, $opts) = @_;
#    $opts ||= {};
#    my $max = $opts->{max};
#
#    return 0 unless ref($data) eq 'HASH';
#    my $i = 0;
#    for my $k (keys %$data) {
#        last if defined($max) && ++$i >= $max;
#        return 0 unless ref($data->{$k}) eq 'ARRAY';
#    }
#    1;
#}
#
#sub is_hoaos {
#    my ($data, $opts) = @_;
#    $opts ||= {};
#    my $max = $opts->{max};
#
#    return 0 unless ref($data) eq 'HASH';
#    my $i = 0;
#    for my $k (keys %$data) {
#        last if defined($max) && ++$i >= $max;
#        return 0 unless is_aos($data->{$k});
#    }
#    1;
#}
#
#sub is_hoh {
#    my ($data, $opts) = @_;
#    $opts ||= {};
#    my $max = $opts->{max};
#
#    return 0 unless ref($data) eq 'HASH';
#    my $i = 0;
#    for my $k (keys %$data) {
#        last if defined($max) && ++$i >= $max;
#        return 0 unless ref($data->{$k}) eq 'HASH';
#    }
#    1;
#}
#
#sub is_hohos {
#    my ($data, $opts) = @_;
#    $opts ||= {};
#    my $max = $opts->{max};
#
#    return 0 unless ref($data) eq 'HASH';
#    my $i = 0;
#    for my $k (keys %$data) {
#        last if defined($max) && ++$i >= $max;
#        return 0 unless is_hos($data->{$k});
#    }
#    1;
#}
#
#1;
## ABSTRACT: Check structure of data
#
#__END__
#
#=pod
#
#=encoding UTF-8
#
#=head1 NAME
#
#Data::Check::Structure - Check structure of data
#
#=head1 VERSION
#
#This document describes version 0.04 of Data::Check::Structure (from Perl distribution Data-Check-Structure), released on 2017-07-18.
#
#=head1 SYNOPSIS
#
#=head1 DESCRIPTION
#
#This small module provides several simple routines to check the structure of
#data, e.g. whether data is an array of arrays ("aoa"), array of scalars ("aos"),
#and so on.
#
#=head1 FUNCTIONS
#
#None exported by default, but they are exportable.
#
#=head2 is_aos($data[, \%opts]) => bool
#
#Check that data is an array of scalars. Examples:
#
# is_aos([]);                     # true
# is_aos(['a', 'b']);             # true
# is_aos(['a', []]);              # false
# is_aos([1,2,3, []], {max=>3});  # true
#
#Known options: C<max> (maximum number of items to check, undef means check all
#items).
#
#=head2 is_aoa($data[, \%opts]) => bool
#
#Check that data is an array of arrays. Examples:
#
# is_aoa([]);                          # true
# is_aoa([[1], [2]]);                  # true
# is_aoa([[1], 'a']);                  # false
# is_aoa([[1],[],[], 'a'], {max=>3});  # true
#
#Known options: C<max> (maximum number of items to check, undef means check all
#items).
#
#=head2 is_aoaos($data[, \%opts]) => bool
#
#Check that data is an array of arrays of scalars. Examples:
#
# is_aoaos([]);                           # true
# is_aoaos([[1], [2]]);                   # true
# is_aoaos([[1], [{}]]);                  # false
# is_aoaos([[1],[],[], [{}]], {max=>3});  # true
#
#Known options: C<max> (maximum number of items to check, undef means check all
#items).
#
#=head2 is_aoh($data[, \%opts]) => bool
#
#Check that data is an array of hashes. Examples:
#
# is_aoh([]);                             # true
# is_aoh([{}, {a=>1}]);                   # true
# is_aoh([{}, 'a']);                      # false
# is_aoh([{},{},{a=>1}, 'a'], {max=>3});  # true
#
#Known options: C<max> (maximum number of items to check, undef means check all
#items).
#
#=head2 is_aohos($data[, \%opts]) => bool
#
#Check that data is an array of hashes of scalars. Examples:
#
# is_aohos([]);                                 # true
# is_aohos([{a=>1}, {}]);                       # true
# is_aohos([{a=>1}, {b=>[]}]);                  # false
# is_aohos([{a=>1},{},{}, {b=>[]}], {max=>3});  # true
#
#Known options: C<max> (maximum number of items to check, undef means check all
#items).
#
#=head2 is_hos($data[, \%opts]) => bool
#
#Check that data is a hash of scalars. Examples:
#
# is_hos({});                                   # true
# is_hos({a=>1, b=>2});                         # true
# is_hos({a=>1, b=>[]});                        # false
# is_hos({a=>1, b=>2, c=>3, d=>[]}, {max=>3});  # true (or false, depending on random hash key ordering)
#
#Known options: C<max> (maximum number of items to check, undef means check all
#items).
#
#=head2 is_hoa($data[, \%opts]) => bool
#
#Check that data is a hash of arrays. Examples:
#
# is_hoa({}) );       # true
# is_hoa({a=>[]}) );  # true
# is_hoa({a=>1}) );   # false
#
#Known options: C<max> (maximum number of items to check, undef means check all
#items).
#
#=head2 is_hoaos($data[, \%opts]) => bool
#
#Check that data is a hash of arrays of scalars. Examples:
#
# is_hoaos({}) );         # true
# is_hoaos({a=>[]}) );    # true
# is_hoaos({a=>[1]}) );   # true
# is_hoaos({a=>1}) );     # false
# is_hoaos({a=>[{}]}) );  # false
#
#Known options: C<max> (maximum number of items to check, undef means check all
#items).
#
#=head2 is_hoh($data[, \%opts]) => bool
#
#Check that data is a hash of hashes. Examples:
#
# is_hoh({}) );       # true
# is_hoh({a=>{}}) );  # true
# is_hoh({a=>1}) );   # false
#
#Known options: C<max> (maximum number of items to check, undef means check all
#items).
#
#=head2 is_hohos($data[, \%opts]) => bool
#
#Check that data is a hash of hashes of scalrs. Examples:
#
# is_hohos({}) );            # true
# is_hohos({a=>{}}) );       # true
# is_hohos({a=>{b=>1}}) );   # true
# is_hohos({a=>1}) );        # false
# is_hohos({a=>{b=>[]}}) );  # false
#
#Known options: C<max> (maximum number of items to check, undef means check all
#items).
#
#=head1 HOMEPAGE
#
#Please visit the project's homepage at L<https://metacpan.org/release/Data-Check-Structure>.
#
#=head1 SOURCE
#
#Source repository is at L<https://github.com/perlancar/perl-Data-Check-Structure>.
#
#=head1 BUGS
#
#Please report any bugs or feature requests on the bugtracker website L<https://rt.cpan.org/Public/Dist/Display.html?Name=Data-Check-Structure>
#
#When submitting a bug or request, please include a test-file or a
#patch to an existing test-file that illustrates the bug or desired
#feature.
#
#=head1 AUTHOR
#
#perlancar <perlancar@cpan.org>
#
#=head1 COPYRIGHT AND LICENSE
#
#This software is copyright (c) 2017, 2014 by perlancar@cpan.org.
#
#This is free software; you can redistribute it and/or modify it under
#the same terms as the Perl 5 programming language system itself.
#
#=cut
### Getopt/Long/EvenLess.pm ###
#package Getopt::Long::EvenLess;
#
#our $DATE = '2017-08-09'; # DATE
#our $VERSION = '0.111'; # VERSION
#
## IFUNBUILT
## # use strict 'subs', 'vars';
## # use warnings;
## END IFUNBUILT
#
#our @EXPORT   = qw(GetOptions);
#our @EXPORT_OK = qw(GetOptionsFromArray);
#
#my $config = {
#    pass_through => 0,
#    auto_abbrev => 1,
#};
#
#sub Configure {
#    my $old_config = { %$config };
#
#    if (ref($_[0]) eq 'HASH') {
#        for (keys %{$_[0]}) {
#            $config->{$_} = $_[0]{$_};
#        }
#    } else {
#        for (@_) {
#            if ($_ eq 'pass_through') {
#                $config->{pass_through} = 1;
#            } elsif ($_ eq 'no_pass_through') {
#                $config->{pass_through} = 0;
#            } elsif ($_ eq 'auto_abbrev') {
#                $config->{auto_abbrev} = 1;
#            } elsif ($_ eq 'no_auto_abbrev') {
#                $config->{auto_abbrev} = 0;
#            } elsif ($_ =~ /\A(no_ignore_case|no_getopt_compat|gnu_compat|bundling|permute)\z/) {
#                # ignore, already behaves that way
#            } else {
#                die "Unknown configuration '$_'";
#            }
#        }
#    }
#    $old_config;
#}
#
#sub import {
#    my $pkg = shift;
#    my $caller = caller;
#    my @imp = @_ ? @_ : @EXPORT;
#    for my $imp (@imp) {
#        if (grep {$_ eq $imp} (@EXPORT, @EXPORT_OK)) {
#            *{"$caller\::$imp"} = \&{$imp};
#        } else {
#            die "$imp is not exported by ".__PACKAGE__;
#        }
#    }
#}
#
#sub GetOptionsFromArray {
#    my ($argv, %spec) = @_;
#
#    my $success = 1;
#
#    my %spec_by_opt_name;
#    for (keys %spec) {
#        my $orig = $_;
#        s/=[fios][@%]?\z//;
#        s/\|.+//;
#        $spec_by_opt_name{$_} = $orig;
#    }
#
#    my $code_find_opt = sub {
#        my ($wanted, $short_mode) = @_;
#        my @candidates;
#      OPT_SPEC:
#        for my $spec (keys %spec) {
#            $spec =~ s/=[fios][@%]?\z//;
#            my @opts = split /\|/, $spec;
#            for my $o (@opts) {
#                next if $short_mode && length($o) > 1;
#                if ($o eq $wanted) {
#                    # perfect match, we immediately go with this one
#                    @candidates = ($opts[0]);
#                    last OPT_SPEC;
#                } elsif ($config->{auto_abbrev} && index($o, $wanted) == 0) {
#                    # prefix match, collect candidates first
#                    push @candidates, $opts[0];
#                    next OPT_SPEC;
#                }
#            }
#        }
#        if (!@candidates) {
#            unless ($config->{pass_through}) {
#                warn "Unknown option: $wanted\n";
#                $success = 0;
#            }
#            return undef; # means unknown
#        } elsif (@candidates > 1) {
#            unless ($config->{pass_through}) {
#                warn "Option $wanted is ambiguous (" .
#                    join(", ", @candidates) . ")\n";
#                $success = 0;
#            }
#            return ''; # means ambiguous
#        }
#        return $candidates[0];
#    };
#
#    my $code_set_val = sub {
#        my $name = shift;
#
#        my $spec_key = $spec_by_opt_name{$name};
#        my $handler  = $spec{$spec_key};
#
#        $handler->({name=>$name}, @_ ? $_[0] : 1);
#    };
#
#    my $i = -1;
#    my @remaining;
#  ELEM:
#    while (++$i < @$argv) {
#        if ($argv->[$i] eq '--') {
#
#            push @remaining, @{$argv}[$i+1 .. @$argv-1];
#            last ELEM;
#
#        } elsif ($argv->[$i] =~ /\A--(.+?)(?:=(.*))?\z/) {
#
#            my ($used_name, $val_in_opt) = ($1, $2);
#            my $opt = $code_find_opt->($used_name);
#            if (!defined($opt)) {
#                # unknown option
#                push @remaining, $argv->[$i];
#                next ELEM;
#            } elsif (!length($opt)) {
#                push @remaining, $argv->[$i];
#                next ELEM; # ambiguous
#            }
#
#            my $spec = $spec_by_opt_name{$opt};
#            # check whether option requires an argument
#            if ($spec =~ /=[fios][@%]?\z/) {
#                if (defined $val_in_opt) {
#                    # argument is taken after =
#                    $code_set_val->($opt, $val_in_opt);
#                } else {
#                    if ($i+1 >= @$argv) {
#                        # we are the last element
#                        warn "Option $used_name requires an argument\n";
#                        $success = 0;
#                        last ELEM;
#                    }
#                    $i++;
#                    $code_set_val->($opt, $argv->[$i]);
#                }
#            } else {
#                $code_set_val->($opt);
#            }
#
#        } elsif ($argv->[$i] =~ /\A-(.*)/) {
#
#            my $str = $1;
#            my $remaining_pushed;
#          SHORT_OPT:
#            while ($str =~ s/(.)//) {
#                my $used_name = $1;
#                my $short_opt = $1;
#                my $opt = $code_find_opt->($short_opt, 'short');
#                if (!defined $opt) {
#                    # unknown short option
#                    push @remaining, "-" unless $remaining_pushed++;
#                    $remaining[-1] .= $short_opt;
#                    next SHORT_OPT;
#                } elsif (!length $opt) {
#                    # ambiguous short option
#                    push @remaining, "-" unless $remaining_pushed++;
#                    $remaining[-1] .= $short_opt;
#                }
#
#                my $spec = $spec_by_opt_name{$opt};
#                # check whether option requires an argument
#                if ($spec =~ /=[fios][@%]?\z/) {
#                    if (length $str) {
#                        # argument is taken from $str
#                        $code_set_val->($opt, $str);
#                        next ELEM;
#                    } else {
#                        if ($i+1 >= @$argv) {
#                            # we are the last element
#                            unless ($config->{pass_through}) {
#                                warn "Option $used_name requires an argument\n";
#                                $success = 0;
#                            }
#                            last ELEM;
#                        }
#                        # take the next element as argument
#                        $i++;
#                        $code_set_val->($opt, $argv->[$i]);
#                    }
#                } else {
#                    $code_set_val->($opt);
#                }
#            }
#
#        } else { # argument
#
#            push @remaining, $argv->[$i];
#            next;
#
#        }
#    }
#
#  RETURN:
#    splice @$argv, 0, ~~@$argv, @remaining; # replace with remaining elements
#    return $success;
#}
#
#sub GetOptions {
#    GetOptionsFromArray(\@ARGV, @_);
#}
#
#1;
## ABSTRACT: Like Getopt::Long::Less, but with even less features
#
#__END__
#
#=pod
#
#=encoding UTF-8
#
#=head1 NAME
#
#Getopt::Long::EvenLess - Like Getopt::Long::Less, but with even less features
#
#=head1 VERSION
#
#This document describes version 0.111 of Getopt::Long::EvenLess (from Perl distribution Getopt-Long-EvenLess), released on 2017-08-09.
#
#=head1 DESCRIPTION
#
#This module (GLEL for short) is a reimplementation of L<Getopt::Long> (GL for
#short), but with much less features. It's an even more stripped down version of
#L<Getopt::Long::Less> (GLL for short) and is perhaps less convenient to use for
#day-to-day scripting work.
#
#The main goal is minimum amount of code and small startup overhead. This module
#is an experiment of how little code I can use to support the stuffs I usually do
#with GL.
#
#Compared to GL and GLL, it:
#
#=over
#
#=item * has minimum Configure() support
#
#Only these configurations are known: pass_through, no_pass_through (default).
#
#GLEL is equivalent to GL in this mode: bundling, no_ignore_case,
#no_getopt_compat, gnu_compat, permute.
#
#No support for configuring via import options e.g.:
#
# use Getopt::Long qw(:config pass_through);
#
#=item * does not support increment (C<foo+>)
#
#=item * no type checking (C<foo=i>, C<foo=f>, C<foo=s> all accept any string)
#
#=item * does not support optional value (C<foo:s>), only no value (C<foo>) or required value (C<foo=s>)
#
#=item * does not support desttypes (C<foo=s@>)
#
#=item * does not support handler other than coderef (so no C<< "foo=s" => \$scalar >>, C<< "foo=s" => \@ary >>, only C<< "foo=s" => sub { ... } >>)
#
#Also, in coderef handler, code will get a simple hash instead of a "callback"
#object as its first argument.
#
#=item * does not support hashref as first argument
#
#=item * does not support bool/negation (no C<foo!>, so you have to declare both C<foo> and C<no-foo> manually)
#
#=back
#
#The result?
#
#B<Amount of code>. GLEL 0.07 is about 175 lines of code, while GL is about 1500.
#Sure, if you I<really> want to be minimalistic, you can use this single line of
#code to get options:
#
# @ARGV = grep { /^--([^=]+)(=(.*))?/ ? ($opts{$1} = $2 ? $3 : 1, 0) : 1 } @ARGV;
#
#and you're already able to extract C<--flag> or C<--opt=val> from C<@ARGV> but
#you also lose a lot of stuffs like autoabbreviation, C<--opt val> syntax support
#syntax (which is more common, but requires you specify an option spec), custom
#handler, etc.
#
#B<Startup overhead>. Here's a sample startup overhead benchmark:
#
#                             Rate        run_gl     load_gl run_gl_evenless load_gl_evenless run_gl_less load_gl_less   perl
# run_gl           64.096+-0.092/s            --       -0.5%          -68.6%           -69.2%      -70.1%       -70.4% -90.6%
# load_gl            64.39+-0.16/s   0.46+-0.29%          --          -68.4%           -69.1%      -70.0%       -70.3% -90.5%
# run_gl_evenless   203.88+-0.74/s   218.1+-1.2% 216.6+-1.4%              --            -2.1%       -4.9%        -5.8% -70.0%
# load_gl_evenless  208.24+-0.57/s     224.9+-1% 223.4+-1.2%     2.14+-0.46%               --       -2.8%        -3.8% -69.4%
# run_gl_less       214.28+-0.62/s   234.3+-1.1% 232.8+-1.3%      5.1+-0.49%       2.9+-0.41%          --        -1.0% -68.5%
# load_gl_less      216.44+-0.45/s 237.68+-0.85% 236.1+-1.1%     6.16+-0.44%      3.94+-0.36% 1.01+-0.36%           -- -68.2%
# perl                679.7+-3.7/s     960.5+-6% 955.7+-6.4%     233.4+-2.2%        226.4+-2%   217.2+-2%  214.1+-1.8%     --
# 
# Average times:
#   perl            :     1.4712ms
#   load_gl_less    :     4.6202ms
#   run_gl_less     :     4.6668ms
#   load_gl_evenless:     4.8022ms
#   run_gl_evenless :     4.9048ms
#   load_gl         :    15.5304ms
#   run_gl          :    15.6016ms
#
#=head1 FUNCTIONS
#
#=head2 Configure(@configs | \%config) => hash
#
#Set configuration. Known configurations:
#
#=over
#
#=item * pass_through
#
#Ignore errors (unknown/ambiguous option) and still make GetOptions return true.
#
#=item * no_pass_through (default)
#
#=item * no_auto_abbrev
#
#=item * auto_abbrev (default)
#
#=item * no_ignore_case
#
#=item * no_getopt_compat
#
#=item * gnu_compat
#
#=item * bundling
#
#=item * permute
#
#=back
#
#Return old configuration data. To restore old configuration data you can pass it
#back to C<Configure()>, e.g.:
#
# my $orig_conf = Getopt::Long::EvenLess::Configure("pass_through");
# # ...
# Getopt::Long::EvenLess::Configure($orig_conf);
#
#=head2 GetOptions(%spec) => bool
#
#Shortcut for:
#
# GetOptionsFromArray(\@ARGV, %spec)
#
#=head2 GetOptionsFromArray(\@ary, %spec) => bool
#
#Get (and strip) options from C<@ary>. Return true on success or false on failure
#(unknown option, etc).
#
#=head1 HOMEPAGE
#
#Please visit the project's homepage at L<https://metacpan.org/release/Getopt-Long-EvenLess>.
#
#=head1 SOURCE
#
#Source repository is at L<https://github.com/perlancar/perl-Getopt-Long-EvenLess>.
#
#=head1 BUGS
#
#Please report any bugs or feature requests on the bugtracker website L<https://rt.cpan.org/Public/Dist/Display.html?Name=Getopt-Long-EvenLess>
#
#When submitting a bug or request, please include a test-file or a
#patch to an existing test-file that illustrates the bug or desired
#feature.
#
#=head1 SEE ALSO
#
#L<Getopt::Long>
#
#L<Getopt::Long::Less>
#
#If you want I<more> features intead of less, try L<Getopt::Long::More>.
#
#=head1 AUTHOR
#
#perlancar <perlancar@cpan.org>
#
#=head1 COPYRIGHT AND LICENSE
#
#This software is copyright (c) 2017, 2016, 2015 by perlancar@cpan.org.
#
#This is free software; you can redistribute it and/or modify it under
#the same terms as the Perl 5 programming language system itself.
#
#=cut
### Local/_pci_check_args.pm ###
#sub _pci_check_args {
#    my ($args) = @_;
#    my $sc_name = $_pci_r->{subcommand_name};
#    if ($sc_name eq "") {
#      FILL_FROM_POS: {
#            1;
#            if (@ARGV > 1) { if (exists $args->{"outfile"}) { return [400, "You specified --outfile but also argument #1"]; } else { $args->{"outfile"} = delete($ARGV[1]); } }
#            if (@ARGV > 0) { if (exists $args->{"infile"}) { return [400, "You specified --infile but also argument #0"]; } else { $args->{"infile"} = delete($ARGV[0]); } }
#        }
#        my @check_argv = @ARGV;
#        # fill from cmdline_src
#
#        # fill defaults from "default" property and check against schema
#        no warnings ('void');
#        require Module::Path::More;
#        my $_sahv_dpath;
#        my $_sahv_err;
#        if (exists $args->{"browser"}) {
#            $_sahv_dpath = [];
#            # req #0
#            ((defined($args->{"browser"})) ? 1 : (($_sahv_err //= "Required but not specified"),0))
#            
#            &&
#            
#            # check type 'bool'
#            ((!ref($args->{"browser"})) ? 1 : (($_sahv_err //= "Not of type boolean value"),0))
#            
#            &&
#            
#            (# clause: is
#            (($args->{"browser"} ? 1:0) == (1 ? 1:0) ? 1 : (($_sahv_err //= "Must have the value 1"),0)))
#             ; if ($_sahv_err) { return [400, "Argument validation failed: $_sahv_err"] }
#        } # if date arg exists
#        $args->{"infile"} //= "-";
#        if (exists $args->{"infile"}) {
#            $_sahv_dpath = [];
#            # req #1
#            ((defined($args->{"infile"})) ? 1 : (($_sahv_err //= "Required but not specified"),0))
#            
#            &&
#            
#            # coerce rule(s): str_convert_perl_pod_or_pm_to_path
#            (($args->{"infile"} = (1) ? (do { my $_sahc_orig = $args->{"infile"}; if ($_sahc_orig =~ m!\A\w+((?:/|::)\w+)*(?:\.pm|\.pod)?\z!) {  (my $tmp = $_sahc_orig) =~ s!/!::!g; my $ext; $tmp =~ s/\.(pm|pod)\z// and $ext = $1;  Module::Path::More::module_path(module=>$tmp, find_pm=>!$ext || $ext eq 'pm' ? 2:0, find_pod=>!$ext || $ext eq 'pod' ? 1:0, find_prefix=>0, find_pmc=>0) || $_sahc_orig } else {  $_sahc_orig } }) : $args->{"infile"}), 1)
#            
#            &&
#            
#            # check type 'str'
#            ((!ref($args->{"infile"})) ? 1 : (($_sahv_err //= "Not of type text"),0))
#             ; if ($_sahv_err) { return [400, "Argument validation failed: $_sahv_err"] }
#        } # if date arg exists
#        if (exists $args->{"outfile"}) {
#            $_sahv_dpath = [];
#            # req #1
#            ((defined($args->{"outfile"})) ? 1 : (($_sahv_err //= "Required but not specified"),0))
#            
#            &&
#            
#            # coerce rule(s): str_strip_trailing_slash
#            (($args->{"outfile"} = (1) ? (do { (my $tmp = $args->{"outfile"}) =~ s!/\z!!g; $tmp }) : $args->{"outfile"}), 1)
#            
#            &&
#            
#            # check type 'str'
#            ((!ref($args->{"outfile"})) ? 1 : (($_sahv_err //= "Not of type text"),0))
#             ; if ($_sahv_err) { return [400, "Argument validation failed: $_sahv_err"] }
#        } # if date arg exists
#
#        # check required args
#        return [400, "Missing required value for argument: browser"] if exists($args->{"browser"}) && !defined($args->{"browser"});
#        return [400, "Missing required value for argument: infile"] if exists($args->{"infile"}) && !defined($args->{"infile"});
#        return [400, "Missing required value for argument: outfile"] if exists($args->{"outfile"}) && !defined($args->{"outfile"});
#        _pci_err([500, "Extraneous command-line argument(s): ".join(", ", @check_argv)]) if @check_argv;
#        [200];
#    } else { _pci_err([500, "Unknown subcommand1: $sc_name"]); }
#}
#1;
### Local/_pci_clean_json.pm ###
#sub _pci_clean_json { require Clone::PP; require Scalar::Util;  use feature 'state'; state $cleanser = sub {
#my $data = shift;
#state %refs;
#state $ctr_circ;
#state $process_array;
#state $process_hash;
#if (!$process_array) { $process_array = sub { my $a = shift; for my $e (@$a) { my $ref=ref($e);
#    if ($ref && $refs{ $e }++) { if (++$ctr_circ <= 1) { $e = Clone::PP::clone($e); redo } else { $e = 'CIRCULAR'; $ref = '' } }
#    elsif ($ref eq 'DateTime') { $e = $e->epoch; $ref = ref($e) }
#    elsif ($ref eq 'Math::BigInt') { $e = $e->bstr; $ref = ref($e) }
#    elsif ($ref eq 'Regexp') { $e = "$e"; $ref = "" }
#    elsif ($ref eq 'SCALAR') { $e = ${ $e }; $ref = ref($e) }
#    elsif ($ref eq 'Time::Moment') { $e = $e->epoch; $ref = ref($e) }
#    elsif ($ref eq 'version') { $e = "$e"; $ref = "" }
#    elsif (Scalar::Util::blessed($e)) { my $reftype = Scalar::Util::reftype($e); $e = $reftype eq "HASH" ? {%{ $e }} : $reftype eq "ARRAY" ? [@{ $e }] : $reftype eq "SCALAR" ? \(my $copy = ${ $e }) : $reftype eq "CODE" ? sub { goto &{ $e } } :(die "Cannot unbless object with type $ref") }
#    my $reftype=Scalar::Util::reftype($e)//"";
#    if ($reftype eq "ARRAY") { $process_array->($e) }
#    elsif ($reftype eq "HASH") { $process_hash->($e) }
#    elsif ($ref) { $e = $ref; $ref = "" }
#} } }
#if (!$process_hash) { $process_hash = sub { my $h = shift; for my $k (keys %$h) { my $ref=ref($h->{$k});
#    if ($ref && $refs{ $h->{$k} }++) { if (++$ctr_circ <= 1) { $h->{$k} = Clone::PP::clone($h->{$k}); redo } else { $h->{$k} = 'CIRCULAR'; $ref = '' } }
#    elsif ($ref eq 'DateTime') { $h->{$k} = $h->{$k}->epoch; $ref = ref($h->{$k}) }
#    elsif ($ref eq 'Math::BigInt') { $h->{$k} = $h->{$k}->bstr; $ref = ref($h->{$k}) }
#    elsif ($ref eq 'Regexp') { $h->{$k} = "$h->{$k}"; $ref = "" }
#    elsif ($ref eq 'SCALAR') { $h->{$k} = ${ $h->{$k} }; $ref = ref($h->{$k}) }
#    elsif ($ref eq 'Time::Moment') { $h->{$k} = $h->{$k}->epoch; $ref = ref($h->{$k}) }
#    elsif ($ref eq 'version') { $h->{$k} = "$h->{$k}"; $ref = "" }
#    elsif (Scalar::Util::blessed($h->{$k})) { my $reftype = Scalar::Util::reftype($h->{$k}); $h->{$k} = $reftype eq "HASH" ? {%{ $h->{$k} }} : $reftype eq "ARRAY" ? [@{ $h->{$k} }] : $reftype eq "SCALAR" ? \(my $copy = ${ $h->{$k} }) : $reftype eq "CODE" ? sub { goto &{ $h->{$k} } } :(die "Cannot unbless object with type $ref") }
#    my $reftype=Scalar::Util::reftype($h->{$k})//"";
#    if ($reftype eq "ARRAY") { $process_array->($h->{$k}) }
#    elsif ($reftype eq "HASH") { $process_hash->($h->{$k}) }
#    elsif ($ref) { $h->{$k} = $ref; $ref = "" }
#} } }
#%refs = (); $ctr_circ=0;
#for ($data) { my $ref=ref($_);
#    if ($ref && $refs{ $_ }++) { if (++$ctr_circ <= 1) { $_ = Clone::PP::clone($_); redo } else { $_ = 'CIRCULAR'; $ref = '' } }
#    elsif ($ref eq 'DateTime') { $_ = $_->epoch; $ref = ref($_) }
#    elsif ($ref eq 'Math::BigInt') { $_ = $_->bstr; $ref = ref($_) }
#    elsif ($ref eq 'Regexp') { $_ = "$_"; $ref = "" }
#    elsif ($ref eq 'SCALAR') { $_ = ${ $_ }; $ref = ref($_) }
#    elsif ($ref eq 'Time::Moment') { $_ = $_->epoch; $ref = ref($_) }
#    elsif ($ref eq 'version') { $_ = "$_"; $ref = "" }
#    elsif (Scalar::Util::blessed($_)) { my $reftype = Scalar::Util::reftype($_); $_ = $reftype eq "HASH" ? {%{ $_ }} : $reftype eq "ARRAY" ? [@{ $_ }] : $reftype eq "SCALAR" ? \(my $copy = ${ $_ }) : $reftype eq "CODE" ? sub { goto &{ $_ } } :(die "Cannot unbless object with type $ref") }
#    my $reftype=Scalar::Util::reftype($_)//"";
#    if ($reftype eq "ARRAY") { $process_array->($_) }
#    elsif ($reftype eq "HASH") { $process_hash->($_) }
#    elsif ($ref) { $_ = $ref; $ref = "" }
#}
#$data
#}
#;; $cleanser->(shift) }
#1;
### Perinci/Result/Format/Lite.pm ###
#package Perinci::Result::Format::Lite;
#
#our $DATE = '2018-07-04'; # DATE
#our $VERSION = '0.274'; # VERSION
#
#use 5.010001;
##IFUNBUILT
## use strict;
## use warnings;
##END IFUNBUILT
#
#use List::Util qw(first max);
#
#use Exporter qw(import);
#our @EXPORT_OK = qw(format);
#
## copy-pasted from List::MoreUtils::PP
#sub firstidx (&@) {
#    my $f = shift;
#    foreach my $i ( 0 .. $#_ )
#        {
#            local *_ = \$_[$i];
#            return $i if $f->();
#        }
#    return -1;
#}
#
#sub _json {
#    state $json = do {
#        if    (eval { require Cpanel::JSON::XS; 1 })   { Cpanel::JSON::XS->new->canonical(1)->convert_blessed->allow_nonref }
#        elsif (eval { require JSON::Tiny::Subclassable; 1 }) { JSON::Tiny::Subclassable->new }
#        elsif (eval { require JSON::PP; 1 })   { JSON::PP->new->canonical(1)->convert_blessed->allow_nonref }
#        else { die "Can't find any JSON module" }
#    };
#    $json;
#};
#
#sub __cleanse {
#    state $cleanser = do {
#        eval { require Data::Clean::JSON; 1 };
#        if ($@) {
#            undef;
#        } else {
#            Data::Clean::JSON->get_cleanser;
#        }
#    };
#    if ($cleanser) {
#        $cleanser->clean_in_place($_[0]);
#    } else {
#        $_[0];
#    }
#}
#
#sub __gen_table {
#    my ($data, $header_row, $resmeta, $format) = @_;
#
#    $resmeta //= {};
#
#    # column names
#    my @columns;
#    if ($header_row) {
#        @columns = @{$data->[0]};
#    } else {
#        @columns = map {"col$_"} 0..@{$data->[0]}-1;
#    }
#
#    my $column_orders; # e.g. [col2, col1, col3, ...]
#  SET_COLUMN_ORDERS: {
#
#        # find column orders from 'table_column_orders' in result metadata (or
#        # from env)
#        my $tcos;
#        if ($ENV{FORMAT_PRETTY_TABLE_COLUMN_ORDERS}) {
#            $tcos = _json->encode($ENV{FORMAT_PRETTY_TABLE_COLUMN_ORDERS});
#        } elsif (my $rfos = ($resmeta->{'cmdline.format_options'} //
#                                 $resmeta->{format_options})) {
#            my $rfo = $rfos->{'text-pretty'} // $rfos->{text} // $rfos->{any};
#            if ($rfo) {
#                $tcos = $rfo->{table_column_orders};
#            }
#        }
#        if ($tcos) {
#            # find an entry in tcos that @columns contains all the columns of
#          COLS:
#            for my $cols (@$tcos) {
#                for my $col (@$cols) {
#                    next COLS unless first {$_ eq $col} @columns;
#                }
#                $column_orders = $cols;
#                last SET_COLUMN_ORDERS;
#            }
#        }
#
#        if ($resmeta->{'table.field_orders'}) {
#            $column_orders = $resmeta->{'table.field_orders'};
#            last SET_COLUMN_ORDERS;
#        }
#
#        # find column orders from table spec
#        $column_orders = $resmeta->{'table.fields'};
#    }
#
#    # reorder each row according to requested column order
#    if ($column_orders) {
#        require Sort::BySpec;
#        my $cmp = Sort::BySpec::cmp_by_spec(spec => $column_orders);
#        # 0->2, 1->0, ... (map column position from unordered to ordered)
#        my @map0 = sort { $cmp->($a->[1], $b->[1]) }
#            map {[$_, $columns[$_]]} 0..$#columns;
#        #use DD; dd \@map0;
#        my @map;
#        for (0..$#map0) {
#            $map[$_] = $map0[$_][0];
#        }
#        #use DD; dd \@map;
#        my $newdata = [];
#        for my $row (@$data) {
#            my @newrow;
#            for (0..$#map) { $newrow[$_] = $row->[$map[$_]] }
#            push @$newdata, \@newrow;
#        }
#        $data = $newdata;
#        my @newcolumns;
#        for (@map) { push @newcolumns, $columns[$_] }
#        @columns = @newcolumns;
#    }
#
#    my @field_idxs; # map column to index in table.fields
#    {
#        my $tff = $resmeta->{'table.fields'} or last;
#        for my $i (0..$#columns) {
#            $field_idxs[$i] = firstidx { $_ eq $columns[$i] } @$tff;
#        }
#    }
#
#    # determine field labels
#    {
#        last unless $header_row && @$data;
#        my $tff = $resmeta->{'table.fields'} or last;
#        my $tfl = $resmeta->{'table.field_labels'};
#        my $tfu = $resmeta->{'table.field_units'};
#        for my $i (0..$#columns) {
#            my $field_idx = $field_idxs[$i];
#            next unless $field_idx >= 0;
#            if ($tfl && defined $tfl->[$field_idx]) {
#                $data->[0][$i] = $tfl->[$field_idx];
#            } elsif ($tfu && defined $tfu->[$field_idx]) {
#                # add field units as label suffix to header (" (UNIT)")
#                $data->[0][$i] .= " ($tfu->[$field_idx])";
#            }
#        }
#    }
#
#  FORMAT_CELLS:
#    {
#        my $tffmt         = $resmeta->{'table.field_formats'};
#        my $tffmt_code    = $resmeta->{'table.field_format_code'};
#        my $tffmt_default = $resmeta->{'table.default_field_format'};
#        last unless $tffmt || $tffmt_code || $tffmt_default;
#
#        my (@fmt_names, @fmt_opts); # key: column index
#        for my $i (0..$#columns) {
#            my $field_idx = $field_idxs[$i];
#            my $fmt = $tffmt_code ? $tffmt_code->($columns[$i]) : undef;
#            $fmt //= $tffmt->[$field_idx] if $field_idx >= 0;
#            $fmt //= $tffmt_default;
#            if (ref $fmt eq 'ARRAY') {
#                $fmt_names[$i] = $fmt->[0];
#                $fmt_opts [$i] = $fmt->[1] // {};
#            } else {
#                $fmt_names[$i] = $fmt;
#                $fmt_opts [$i] = {};
#            }
#        }
#
#        my $nf;
#
#        for my $i (0..$#{$data}) {
#            next if $i==0 && $header_row;
#            my $row = $data->[$i];
#            for my $j (0..$#columns) {
#                next unless defined $row->[$j];
#                my $fmt_name = $fmt_names[$j];
#                #say "D:j=$j fmt_name=$fmt_name";
#                next unless $fmt_name;
#                my $fmt_opts = $fmt_opts [$j];
#                if ($fmt_name eq 'iso8601_datetime' || $fmt_name eq 'iso8601_date') {
#                    if ($row->[$j] =~ /\A[0-9]+(\.[0-9]*)?\z/) {
#                        my $frac = $1 ? "0$1"+0 : 0;
#                        my @t = gmtime($row->[$j]);
#                        if ($fmt_name eq 'iso8601_datetime') {
#                            $row->[$j] = sprintf(
#                                "%04d-%02d-%02dT%02d:%02d:".($frac ? "%06.3f" : "%02d")."Z",
#                                $t[5]+1900, $t[4]+1, $t[3], $t[2], $t[1], $t[0]+$frac);
#                        } else {
#                            $row->[$j] = sprintf(
#                                "%04d-%02d-%02d",
#                                $t[5]+1900, $t[4]+1, $t[3]);
#                        }
#                    }
#                } elsif ($fmt_name eq 'boolstr') {
#                    $row->[$j] = $row->[$j] ? "yes" : "no";
#                } elsif ($fmt_name eq 'sci2dec') {
#                    if ($row->[$j] =~ /\A(?:[+-]?)(?:\d+\.|\d*\.(\d+))[eE]([+-]?\d+)\z/) {
#                        my $n = length($1 || "") - $2; $n = 0 if $n < 0;
#                        $row->[$j] = sprintf("%.${n}f", $row->[$j]);
#                    }
#                } elsif ($fmt_name eq 'percent') {
#                    my $fmt = $fmt_opts->{sprintf} // '%.2f%%';
#                    $row->[$j] = sprintf($fmt, $row->[$j] * 100);
#                } elsif ($fmt_name eq 'number') {
#                    require Number::Format::BigFloat;
#                    $row->[$j] = Number::Format::BigFloat::format_number(
#                        $row->[$j], {
#                            thousands_sep  => $fmt_opts->{thousands_sep} // ',',
#                            decimal_point  => $fmt_opts->{decimal_point} // '.',
#                            decimal_digits => $fmt_opts->{precision} // 0,
#                            # XXX decimal_fill
#                        });
#                }
#            }
#        }
#    }
#
#    if ($format eq 'text-pretty') {
#      ALIGN_COLUMNS:
#        {
#            # XXX we just want to turn off 'uninitialized' and 'negative repeat
#            # count does nothing' from the operator x
#            no warnings;
#
#            my $tfa         = $resmeta->{'table.field_aligns'};
#            my $tfa_code    = $resmeta->{'table.field_align_code'};
#            my $tfa_default = $resmeta->{'table.default_field_align'};
#            last unless $tfa || $tfa_code || $tfa_default;
#            last unless @$data;
#
#            for my $colidx (0..$#columns) {
#                my $field_idx = $field_idxs[$colidx];
#                my $align = $tfa_code ? $tfa_code->($columns[$colidx]) : undef;
#                $align //= $tfa->[$field_idx] if $field_idx >= 0;
#                $align //= $tfa_default;
#                next unless $align;
#
#                # determine max widths
#                my $maxw;
#                my ($maxw_bd, $maxw_d, $maxw_ad); # before digit, digit, after d
#                if ($align eq 'number') {
#                    my (@w_bd, @w_d, @w_ad);
#                    for my $i (0..$#{$data}) {
#                        my $row = $data->[$i];
#                        if (@$row > $colidx) {
#                            my $cell = $row->[$colidx];
#                            if ($header_row && $i == 0) {
#                                my $w = length($cell);
#                                push @w_bd, 0;
#                                push @w_bd, 0;
#                                push @w_ad, 0;
#                            } elsif ($cell =~ /\A([+-]?\d+)(\.?)(\d*)\z/) {
#                                # decimal notation number
#                                push @w_bd, length($1);
#                                push @w_d , length($2);
#                                push @w_ad, length($3);
#                            } elsif ($cell =~ /\A([+-]?\d+\.?\d*)([eE])([+-]?\d+)\z/) {
#                                # scientific notation number
#                                push @w_bd, length($1);
#                                push @w_d , length($2);
#                                push @w_ad, length($3);
#                            } else {
#                                # not a number
#                                push @w_bd, length($cell);
#                                push @w_bd, 0;
#                                push @w_ad, 0;
#                            }
#                        } else {
#                            push @w_bd, 0;
#                            push @w_d , 0;
#                            push @w_ad, 0;
#                        }
#                    }
#                    $maxw_bd = max(@w_bd);
#                    $maxw_d  = max(@w_d);
#                    $maxw_ad = max(@w_ad);
#                    if ($header_row) {
#                        my $w = length($data->[0][$colidx]);
#                        if ($maxw_d == 0 && $maxw_ad == 0) {
#                            $maxw_bd = $w;
#                        }
#                    }
#                }
#
#                $maxw = max(map {
#                    @$_ > $colidx ? length($_->[$colidx]) : 0
#                } @$data);
#
#                # do the alignment
#                for my $i (0..$#{$data}) {
#                    my $row = $data->[$i];
#                    for my $i (0..$#{$data}) {
#                        my $row = $data->[$i];
#                        next unless @$row > $colidx;
#                        my $cell = $row->[$colidx];
#                        next unless defined($cell);
#                        if ($align eq 'number') {
#                            my ($bd, $d, $ad);
#                            if ($header_row && $i == 0) {
#                            } elsif (($bd, $d, $ad) = $cell =~ /\A([+-]?\d+)(\.?)(\d*)\z/) {
#                                $cell = join(
#                                    '',
#                                    (' ' x ($maxw_bd - length($bd))), $bd,
#                                    $d , (' ' x ($maxw_d  - length($d ))),
#                                    $ad, (' ' x ($maxw_ad - length($ad))),
#                                );
#                            } elsif (($bd, $d, $ad) = $cell =~ /\A([+-]?\d+\.?\d*)([eE])([+-]?\d+)\z/) {
#                                $cell = join(
#                                    '',
#                                    (' ' x ($maxw_bd - length($bd))), $bd,
#                                    $d , (' ' x ($maxw_d  - length($d ))),
#                                    $ad, (' ' x ($maxw_ad - length($ad))),
#                                );
#                            }
#                            my $w = length($cell);
#                            $cell = (' ' x ($maxw - $w)) . $cell
#                                if $maxw > $w;
#                        } elsif ($align eq 'right') {
#                            $cell = (' ' x ($maxw - length($cell))) . $cell;
#                        } elsif ($align eq 'middle' || $align eq 'center') {
#                            my $w = length($cell);
#                            my $n = int(($maxw-$w)/2);
#                            $cell = (' ' x $n) . $cell . (' ' x ($maxw-$w-$n));
#                        } else {
#                            # assumed left
#                            $cell .= (' ' x ($maxw - length($cell)));
#
#                        }
#                        $row->[$colidx] = $cell;
#                    }
#                }
#            } # for $colidx
#        } # END align columns
#
#        my $fres;
#        if (my $backend = $ENV{FORMAT_PRETTY_TABLE_BACKEND}) {
#            require Text::Table::Any;
#            $fres = Text::Table::Any::table(rows=>$data, header_row=>$header_row, backend=>$backend);
#        } else {
#            require Text::Table::Tiny;
#            $fres = Text::Table::Tiny::table(rows=>$data, header_row=>$header_row);
#        }
#        $fres .= "\n" unless $fres =~ /\R\z/ || !length($fres);
#        $fres;
#    } elsif ($format eq 'csv') {
#        no warnings 'uninitialized';
#        join(
#            "",
#            map {
#                my $row = $_;
#                join(
#                    ",",
#                    map {
#                        my $cell = $_;
#                        $cell =~ s/(["\\])/\\$1/g;
#                        qq("$cell");
#                    } @$row)."\n";
#            } @$data
#        );
#    } elsif ($format eq 'html') {
#        no warnings 'uninitialized';
#        require HTML::Entities;
#
#        my $tfa = $resmeta->{'table.field_aligns'};
#
#        my @res;
#        push @res, "<table".($resmeta->{'table.html_class'} ?
#                                 " class=\"".HTML::Entities::encode_entities(
#                                     $resmeta->{'table.html_class'})."\"" : "").
#                                         ">\n";
#        for my $i (0..$#{$data}) {
#            my $data_elem = $i == 0 ? "th" : "td";
#            push @res, "<thead>\n" if $i == 0;
#            push @res, "<tbody>\n" if $i == 1;
#            push @res, " <tr>\n";
#            my $row = $data->[$i];
#            for my $j (0..$#{$row}) {
#                my $field_idx = $field_idxs[$j];
#                my $align;
#                if ($field_idx >= 0 && $tfa->[$field_idx]) {
#                    $align = $tfa->[$field_idx];
#                    $align = "right" if $align eq 'number';
#                    $align = "middle" if $align eq 'center';
#                }
#                push @res, "  <$data_elem",
#                    ($align ? " align=\"$align\"" : ""),
#                    ">", HTML::Entities::encode_entities($row->[$j]),
#                    "</$data_elem>\n";
#            }
#            push @res, " </tr>\n";
#            push @res, "</thead>\n" if $i == 0;
#        }
#        push @res, "</tbody>\n";
#        push @res, "</table>\n";
#        join '', @res;
#    } else {
#        no warnings 'uninitialized';
#        shift @$data if $header_row;
#        join("", map {join("\t", @$_)."\n"} @$data);
#    }
#}
#
#sub format {
#    my ($res, $format, $is_naked, $cleanse) = @_;
#
#    if ($format =~ /\A(text|text-simple|text-pretty|csv|html)\z/) {
#        $format = $format eq 'text' ?
#            ((-t STDOUT) ? 'text-pretty' : 'text-simple') : $format;
#        no warnings 'uninitialized';
#        if ($res->[0] !~ /^(2|304)/) {
#            my $fres = "ERROR $res->[0]: $res->[1]";
#            if (my $prev = $res->[3]{prev}) {
#                $fres .= " ($prev->[0]: $prev->[1])";
#            }
#            return "$fres\n";
#        } elsif ($res->[3] && $res->[3]{"x.hint.result_binary"}) {
#            return $res->[2];
#        } else {
#            require Data::Check::Structure;
#            my $data = $res->[2];
#            my $max = 5;
#            if (!ref($data)) {
#                $data //= "";
#                $data .= "\n" unless !length($data) || $data =~ /\n\z/;
#                return $data;
#            } elsif (ref($data) eq 'ARRAY' && !@$data) {
#                return "";
#            } elsif (Data::Check::Structure::is_aos($data, {max=>$max})) {
#                return join("", map {"$_\n"} @$data);
#            } elsif (Data::Check::Structure::is_aoaos($data, {max=>$max})) {
#                my $header_row = 0;
#                my $data = $data;
#                if ($res->[3]{'table.fields'}) {
#                    $data = [$res->[3]{'table.fields'}, @$data];
#                    $header_row = 1;
#                }
#                return __gen_table($data, $header_row, $res->[3], $format);
#            } elsif (Data::Check::Structure::is_hos($data, {max=>$max})) {
#                $data = [map {[$_, $data->{$_}]} sort keys %$data];
#                unshift @$data, ["key", "value"];
#                return __gen_table($data, 1, $res->[3], $format);
#            } elsif (Data::Check::Structure::is_aohos($data, {max=>$max})) {
#                # collect all mentioned fields
#                my @fieldnames;
#                if ($res->[3] && $res->[3]{'table.fields'} &&
#                        $res->[3]{'table.hide_unknown_fields'}) {
#                    @fieldnames = @{ $res->[3]{'table.fields'} };
#                } else {
#                    my %fieldnames;
#                    for my $row (@$data) {
#                        $fieldnames{$_}++ for keys %$row;
#                    }
#                    @fieldnames = sort keys %fieldnames;
#                }
#                my $newdata = [];
#                for my $row (@$data) {
#                    push @$newdata, [map {$row->{$_}} @fieldnames];
#                }
#                unshift @$newdata, \@fieldnames;
#                return __gen_table($newdata, 1, $res->[3], $format);
#            } else {
#                $format = 'json-pretty';
#            }
#        }
#    }
#
#    my $tff = $res->[3]{'table.fields'};
#    $res = $res->[2] if $is_naked;
#
#    if ($format eq 'perl') {
#        my $use_color = $ENV{COLOR} // (-t STDOUT);
#        if ($use_color && eval { require Data::Dump::Color; 1 }) {
#            return Data::Dump::Color::dump($res);
#        } elsif (eval { require Data::Dump; 1 }) {
#            return Data::Dump::dump($res);
#        } else {
#            no warnings 'once';
#            require Data::Dumper;
#            local $Data::Dumper::Terse = 1;
#            local $Data::Dumper::Indent = 1;
#            local $Data::Dumper::Useqq = 1;
#            local $Data::Dumper::Deparse = 1;
#            local $Data::Dumper::Quotekeys = 0;
#            local $Data::Dumper::Sortkeys = 1;
#            local $Data::Dumper::Trailingcomma = 1;
#            return Data::Dumper::Dumper($res);
#        }
#    }
#
#    unless ($format =~ /\Ajson(-pretty)?\z/) {
#        warn "Unknown format '$format', fallback to json-pretty";
#        $format = 'json-pretty';
#    }
#    __cleanse($res) if ($cleanse//1);
#    if ($format =~ /json/) {
#        if ($tff && _json->can("sort_by") &&
#                eval { require Sort::ByExample; 1}) {
#            my $cmp = Sort::ByExample->cmp($tff);
#            _json->sort_by(sub { $cmp->($JSON::PP::a, $JSON::PP::b) });
#        }
#
#        if ($format eq 'json') {
#            return _json->encode($res) . "\n";
#        } else {
#            _json->pretty(1);
#            return _json->encode($res);
#        }
#    }
#}
#
#1;
## ABSTRACT: Format enveloped result
#
#__END__
#
#=pod
#
#=encoding UTF-8
#
#=head1 NAME
#
#Perinci::Result::Format::Lite - Format enveloped result
#
#=head1 VERSION
#
#This document describes version 0.274 of Perinci::Result::Format::Lite (from Perl distribution Perinci-Result-Format-Lite), released on 2018-07-04.
#
#=head1 SYNOPSIS
#
#=head1 DESCRIPTION
#
#=for Pod::Coverage ^(firstidx)$
#
#=head1 FUNCTIONS
#
#=head2 format($res, $format[ , $is_naked=0, $cleanse=1 ]) => str
#
#=head1 ENVIRONMENT
#
#=head2 FORMAT_PRETTY_TABLE_BACKEND => str
#
#If this is set, will render text table using L<Text::Table::Any> (with
#C<backend> set to the value of this environment variable) instead of the default
#L<Text::Table::Tiny>. This is useful if you want to output text table in a
#different format, for example to generate Org tables (make sure
#L<Text::Table::Org> backend is already installed):
#
# % FORMAT_PRETTY_TABLE_BACKEND=Text::Table::Org lcpan rdeps Getopt::Lucid
#
#=head2 FORMAT_PRETTY_TABLE_COLUMN_ORDERS => array (json)
#
#Set the default of C<table_column_orders> in C<format_options> in result
#metadata, similar to what's implemented in L<Perinci::Result::Format> and
#L<Data::Format::Pretty::Console>.
#
#=head2 COLOR => bool
#
#=head1 HOMEPAGE
#
#Please visit the project's homepage at L<https://metacpan.org/release/Perinci-Result-Format-Lite>.
#
#=head1 SOURCE
#
#Source repository is at L<https://github.com/perlancar/perl-Perinci-Result-Format-Lite>.
#
#=head1 BUGS
#
#Please report any bugs or feature requests on the bugtracker website L<https://rt.cpan.org/Public/Dist/Display.html?Name=Perinci-Result-Format-Lite>
#
#When submitting a bug or request, please include a test-file or a
#patch to an existing test-file that illustrates the bug or desired
#feature.
#
#=head1 SEE ALSO
#
#L<Perinci::Result::Format>, a more heavyweight version of this module.
#
#L<Perinci::CmdLine::Lite> uses this module to format enveloped result.
#
#=head1 AUTHOR
#
#perlancar <perlancar@cpan.org>
#
#=head1 COPYRIGHT AND LICENSE
#
#This software is copyright (c) 2018, 2017, 2016, 2015 by perlancar@cpan.org.
#
#This is free software; you can redistribute it and/or modify it under
#the same terms as the Perl 5 programming language system itself.
#
#=cut
### Text/Table/Tiny.pm ###
#use 5.006;
#use strict;
#use warnings;
#package Text::Table::Tiny;
#$Text::Table::Tiny::VERSION = '0.04';
#use parent 'Exporter';
#use List::Util qw();
#
#our @EXPORT_OK = qw/ generate_table /;
#
## ABSTRACT: makes simple tables from two-dimensional arrays, with limited templating options
#
#
#our $COLUMN_SEPARATOR = '|';
#our $ROW_SEPARATOR = '-';
#our $CORNER_MARKER = '+';
#our $HEADER_ROW_SEPARATOR = '=';
#our $HEADER_CORNER_MARKER = 'O';
#
#sub generate_table {
#
#    my %params = @_;
#    my $rows = $params{rows} or die "Must provide rows!";
#
#    # foreach col, get the biggest width
#    my $widths = _maxwidths($rows);
#    my $max_index = _max_array_index($rows);
#
#    # use that to get the field format and separators
#    my $format = _get_format($widths);
#    my $row_sep = _get_row_separator($widths);
#    my $head_row_sep = _get_header_row_separator($widths);
#
#    # here we go...
#    my @table;
#    push @table, $row_sep;
#
#    # if the first row's a header:
#    my $data_begins = 0;
#    if ( $params{header_row} ) {
#        my $header_row = $rows->[0];
#        $data_begins++;
#        push @table, sprintf(
#                         $format, 
#                         map { defined($header_row->[$_]) ? $header_row->[$_] : '' } (0..$max_index)
#                     );
#        push @table, $params{separate_rows} ? $head_row_sep : $row_sep;
#    }
#
#    # then the data
#    foreach my $row ( @{ $rows }[$data_begins..$#$rows] ) {
#        push @table, sprintf(
#	    $format, 
#	    map { defined($row->[$_]) ? $row->[$_] : '' } (0..$max_index)
#	);
#        push @table, $row_sep if $params{separate_rows};
#    }
#
#    # this will have already done the bottom if called explicitly
#    push @table, $row_sep unless $params{separate_rows};
#    return join("\n",grep {$_} @table);
#}
#
#sub _get_cols_and_rows ($) {
#    my $rows = shift;
#    return ( List::Util::max( map { scalar @$_ } @$rows), scalar @$rows);
#}
#
#sub _maxwidths {
#    my $rows = shift;
#    # what's the longest array in this list of arrays?
#    my $max_index = _max_array_index($rows);
#    my $widths = [];
#    for my $i (0..$max_index) {
#        # go through the $i-th element of each array, find the longest
#        my $max = List::Util::max(map {defined $$_[$i] ? length($$_[$i]) : 0} @$rows);
#        push @$widths, $max;
#    }
#    return $widths;
#}
#
## return highest top-index from all rows in case they're different lengths
#sub _max_array_index {
#    my $rows = shift;
#    return List::Util::max( map { $#$_ } @$rows );
#}
#
#sub _get_format {
#    my $widths = shift;
#    return "$COLUMN_SEPARATOR ".join(" $COLUMN_SEPARATOR ",map { "%-${_}s" } @$widths)." $COLUMN_SEPARATOR";
#}
#
#sub _get_row_separator {
#    my $widths = shift;
#    return "$CORNER_MARKER$ROW_SEPARATOR".join("$ROW_SEPARATOR$CORNER_MARKER$ROW_SEPARATOR",map { $ROW_SEPARATOR x $_ } @$widths)."$ROW_SEPARATOR$CORNER_MARKER";
#}
#
#sub _get_header_row_separator {
#    my $widths = shift;
#    return "$HEADER_CORNER_MARKER$HEADER_ROW_SEPARATOR".join("$HEADER_ROW_SEPARATOR$HEADER_CORNER_MARKER$HEADER_ROW_SEPARATOR",map { $HEADER_ROW_SEPARATOR x $_ } @$widths)."$HEADER_ROW_SEPARATOR$HEADER_CORNER_MARKER";
#}
#
## Back-compat: 'table' is an alias for 'generate_table', but isn't exported
#*table = \&generate_table;
#
#1;
#
#__END__
#
#=pod
#
#=head1 NAME
#
#Text::Table::Tiny - simple text tables from 2D arrays, with limited templating options
#
#=head1 SYNOPSIS
#
#    use Text::Table::Tiny 0.04 qw/ generate_table /;
#
#    my $rows = [
#        # header row
#        ['Name', 'Rank', 'Serial'],
#        # rows
#        ['alice', 'pvt', '123456'],
#        ['bob',   'cpl', '98765321'],
#        ['carol', 'brig gen', '8745'],
#    ];
#    print generate_table(rows => $rows, header_row => 1);
#
#
#=head1 DESCRIPTION
#
#This module provides a single function, C<generate_table>, which formats
#a two-dimensional array of data as a text table.
#
#The example shown in the SYNOPSIS generates the following table:
#
#    +-------+----------+----------+
#    | Name  | Rank     | Serial   |
#    +-------+----------+----------+
#    | alice | pvt      | 123456   |
#    | bob   | cpl      | 98765321 |
#    | carol | brig gen | 8745     |
#    +-------+----------+----------+
#
#B<NOTE>: the interface changed with version 0.04, so if you
#use the C<generate_table()> function illustrated above,
#then you need to require at least version 0.04 of this module,
#as shown in the SYNOPSIS.
#
#
#=head2 OPTIONS
#
#The C<generate_table> function understands three arguments,
#which are passed as a hash.
#
#=over 4
#
#
#=item *
#
#rows
#
#Takes an array reference which should contain one or more rows
#of data, where each row is an array reference.
#
#
#=item *
#
#header_row
#
#If given a true value, the first row in the data will be interpreted
#as a header row, and separated from the rest of the table with a ruled line.
#
#
#=item *
#
#separate_rows
#
#If given a true value, a separator line will be drawn between every row in
#the table,
#and a thicker line will be used for the header separator.
#
#
#=back
#
#
#=head2 EXAMPLES
#
#If you just pass the data and no other options:
#
# generate_table(rows => $rows);
#
#You get minimal ruling:
#
#    +-------+----------+----------+
#    | Name  | Rank     | Serial   |
#    | alice | pvt      | 123456   |
#    | bob   | cpl      | 98765321 |
#    | carol | brig gen | 8745     |
#    +-------+----------+----------+
#
#If you want lines between every row, and also want a separate header:
#
# generate_table(rows => $rows, header_row => 1, separate_rows => 1);
#
#You get the maximally ornate:
#
#    +-------+----------+----------+
#    | Name  | Rank     | Serial   |
#    O=======O==========O==========O
#    | alice | pvt      | 123456   |
#    +-------+----------+----------+
#    | bob   | cpl      | 98765321 |
#    +-------+----------+----------+
#    | carol | brig gen | 8745     |
#    +-------+----------+----------+
#
#=head1 FORMAT VARIABLES
#
#You can set a number of package variables inside the C<Text::Table::Tiny> package
#to configure the appearance of the table.
#This interface is likely to be deprecated in the future,
#and some other mechanism provided.
#
#=over 4
#
#=item *
#
#$Text::Table::Tiny::COLUMN_SEPARATOR = '|';
#
#=item *
#
#$Text::Table::Tiny::ROW_SEPARATOR = '-';
#
#=item *
#
#$Text::Table::Tiny::CORNER_MARKER = '+';
#
#=item *
#
#$Text::Table::Tiny::HEADER_ROW_SEPARATOR = '=';
#
#=item *
#
#$Text::Table::Tiny::HEADER_CORNER_MARKER = 'O';
#
#=back
#
#
#=head1 PREVIOUS INTERFACE
#
#Prior to version 0.04 this module provided a function called C<table()>,
#which wasn't available for export. It took exactly the same arguments:
#
# use Text::Table::Tiny;
# my $rows = [ ... ];
# print Text::Table::Tiny::table(rows => $rows, separate_rows => 1, header_row => 1);
#
#For backwards compatibility this interface is still supported.
#The C<table()> function isn't available for export though.
#
#
#=head1 SEE ALSO
#
#There are many modules for formatting text tables on CPAN.
#A good number of them are listed in the
#L<See Also|https://metacpan.org/pod/Text::Table::Manifold#See-Also>
#section of the documentation for L<Text::Table::Manifold>.
#
#
#=head1 REPOSITORY
#
#L<https://github.com/neilb/Text-Table-Tiny>
#
#
#=head1 AUTHOR
#
#Creighton Higgins <chiggins@chiggins.com>
#
#Now maintained by Neil Bowers <neilb@cpan.org>
#
#=head1 COPYRIGHT AND LICENSE
#
#This software is copyright (c) 2012 by Creighton Higgins.
#
#This is free software; you can redistribute it and/or modify it under
#the same terms as the Perl 5 programming language system itself.
#
#=cut
#
