The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!perl

use 5.010;
use strict;
use warnings;

use Getopt::Long;
use Perinci::CmdLine;

our $VERSION = '1.16'; # VERSION

my %opts = (
    library => [],
    subcommands => [],
    pass_cmdline_object => 0,
);
Getopt::Long::Configure('pass_through', 'no_permute');
GetOptions(
    'library|I=s' => $opts{library},
    'help|h|?' => \$opts{help},
    'subcommand|s=s' => $opts{subcommands},
    'pass-cmdline-object|o' => \$opts{pass_cmdline_object},
);

my $me = $0; $me =~ s!.+/!!;

my $oscs = $opts{subcommands};
if ($opts{help} || !@ARGV && !@$oscs) {
    print <<USAGE;
$me - Run commands (from any Riap function) on the command-line

Usage:
  $me --help
  $me [common options] <command def> [subcommand] [action]

*Common options* include: `--library` (`-I`) to add directory to Perl search dir
(a la Perl's `-I`), can be specified multiple times. There is also
`--pass-cmdline-object` (`-o`) if you want to pass Perinci::CmdLine object to
the function.

*Command def* is either a single command definition or multiple subcommands
definition. To define a single command, you can specify a Riap function URL
(e.g. '/Foo/Bar/func'). For multiple subcommands, you can specify a Riap package
URL (e.g. '/Foo/Bar/') or a Perl module name (e.g. 'Foo::Bar'), in which case
all functions in the module/package will be listed and added as subcommands.
Alternatively, you can specify each subcommand separately using '--subcommand'
('-s'). For example, the command below specifies two subcommands called 'func1'
and 'altname':

  $me -s /Foo/Bar/func1 -s "/Foo/Bar/func2 altname"

*Subcommand* picks a subcommand name, required only when you specify multiple
subcommands.

*action* is either '--help' to get help message on the command or subcommand,
'--list' to list subcommands, '--version' to get version, or zero or more
command (function) arguments.

Examples:
  List all subcommands (functions) in a module:
    $me Foo::Bar -l

  Show usage for a subcommand (function):
    $me Foo::Bar func1 --help
    $me /Foo/Bar/func1 -h

  Execute a command and display the result as YAML:
    $me http://example.org/api/Foo/Bar/func --format=yaml --arg 12

  Execute a subcommand:
    $me -s "/Foo/Bar/func1 sc1" -s "/Foo/Baz/func1 sc2" sc2 --arg 12

Notes:
* This is just a simple generic front-end for 'Perinci::CmdLine'. For more
  options/customizations, use or subclass the module directly.

TODO:
* HTTP authentication parameters

USAGE
    exit 0;
}

for my $dir (@{ $opts{library} }) { require lib; lib->import($dir) }

my $cmd = Perinci::CmdLine->new(
    pass_cmdline_object => $opts{pass_cmdline_object},
);

if (@ARGV && !@$oscs) {

    # a single URL is specified.

    my $url = shift @ARGV;
    my $type;

    # Perl module (Foo::Bar) is specified, change it to /Foo/Bar/
    if ($url =~ /\A\w+::(?:\w+)*\z/) {
        $url =~ s!::!/!;
        $url = "/$url/";
        $type = 'package';
    } else {
        # try to find out the entity type
        my $res = $cmd->_pa->request(info => $url);
        die "Can't 'info' $url: $res->[0] - $res->[1]\n"
            unless $res->[0] == 200;
        $type = $res->[2]{type} // '';
        die "Please specify URL to a function or package, not '$type': $url\n"
            unless $type eq 'function' || $type eq 'package';
    }
    $cmd->url($url);
    $cmd->program_name("$me $url")
        unless defined($ENV{PERINCI_CMDLINE_PROGRAM_NAME});
    if ($type eq 'package') {
        my $res = $cmd->_pa->request(list => $url, {detail=>1});
        die "Can't 'list' $url: $res->[0] - $res->[1]\n"
            unless $res->[0] == 200;
        my $scs = {};
        for my $e (@{$res->[2]}) {
            next unless $e->{type} eq 'function';
            my $sub = $e->{uri}; $sub =~ s!.+/!!;
            $scs->{$sub} = {
                url => $e->{uri},
                summary => $e->{summary},
            };
        }
        $cmd->subcommands($scs);
    }

} elsif (@$oscs) {

    # one or more subcommand URLs are specified.

    my $scs = {};
    for my $item (@$oscs) {
        my ($url, $name);
        if ($item =~ /\s/) {
            ($url, $name) = split /\s+/, $item, 2;
        } else {
            $url = $item;
            my $i = 1;
            my $leaf = $url; $leaf =~ s!.+/(.+)!$1!; $leaf ||= "func";
            while (1) {
                $name = $leaf . ($i > 1 ? $i : "");
                last unless $scs->{$name};
                $i++;
            }
        }
        $scs->{$name} = {url => $url};
    }
    $cmd->subcommands($scs);

} else {

    die "BUG: This shouldn't be reached";

}

$cmd->run;

#ABSTRACT: Run commands (from any Riap function) on the command-line
#PODNAME: peri-run

__END__

=pod

=encoding UTF-8

=head1 NAME

peri-run - Run commands (from any Riap function) on the command-line

=head1 VERSION

This document describes version 1.16 of peri-run (from Perl distribution Perinci-CmdLine), released on 2014-07-03.

=head1 SYNOPSIS

Type C<peri-run --help> for more help.

=head1 OPTIONS

=head2 --library, -I

=head2 --help

=head2 --subcommand, -s

=head2 --pass-cmdline-object, -o

=head1 SEE ALSO

L<Perinci::CmdLine>

=head1 HOMEPAGE

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

=head1 SOURCE

Source repository is at L<https://github.com/sharyanto/perl-Perinci-CmdLine>.

=head1 BUGS

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

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

Steven Haryanto <stevenharyanto@gmail.com>

=head1 COPYRIGHT AND LICENSE

This software is copyright (c) 2014 by Steven Haryanto.

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