package Perinci::CmdLine::Manual::Examples; # just to make podweaver happy

# DATE
# VERSION

1;
# ABSTRACT: Collection of examples

__END__

=pod

=encoding UTF-8

=head1 NAME

Perinci::CmdLine::Manual::Examples - Collection of examples

=head1 VERSION

This document describes version 0.25 of Perinci::CmdLine::Manual::Examples (from Perl distribution Perinci-CmdLine-Lite), released on 2014-09-03.

=head1 DESCRIPTION

In the examples, L<Perinci::CmdLine::Any> is used to show examples that are
applicable to either L<Perinci::CmdLine> or L<Perinci::CmdLine::Lite>. For
examples that are more appropriate or only applicable to specific
implementation, the specific module will be used.

Perinci::CmdLine is hereby referred to as C<P::C>, while Perinci::CmdLine as
C<P::C::Lite>.

=head2 Simplest application

Since Perinci::CmdLine is function- and metadata-based, you need to create at
least one function and add some metadata for it. And you'll need to return the
result as an enveloped response. The simplest is something like:

 #!perl
 use strict;
 use warnings;
 use Perinci::CmdLine::Any;

 our %SPEC;
 $SPEC{hello} = {
     v => 1.1,
     summary => 'Say hello',
 };
 sub hello {
     [200, "OK", "Hello, world!"];
 }

 Perinci::CmdLine::Any->new(url => '/main/hello')->run;

The C<url> attribute specifies the location of the function in URL format (see
L<Riap> for more details on the syntax of URL). It is basically a
fully-qualified function name, with C<::> replaced with C</>. With this
URL-based syntax, it is possible to use a remote and/or non-Perl function for
the CLI application.

The hash in C<$SPEC{hello}> is called a L<Rinci> metadata. The keys are called
properties. There are two properties: C<v> (which is always required with the
value of 1.1 to specify version) and C<summary> (which is actually optional, to
describe the function).

In this example, the function and its metadata is put inside the same script.
You can of course put them in a separate Perl module, and use them with e.g. C<<
url => '/Your/Module/func' >>. It is also worth mentioning that if you use the
P::C framework, your functions can also be used directly by other Perl
modules/code since they are just regular Perl functions.

The function returns a 3-element array containing HTTP-like status code, a
status message, and the actual result.

If you save the above script as C<hello> run it on the command-line:

 % ./hello
 Hello, world!

Yup, not very interesting. You get help message for free:

 % ./hello --help
 % ./hello -h

As well as some common options like C<--format> to return the result in a
different format:

 % ./hello --json
 [200,"OK","Hello, world!"]

 % ./hello --format perl; # only in P::C, not available in P::C::Lite
 [200, "OK", "Hello, world!"]

=head2 Function arguments (command-line options)

Function arguments map to command-line options. Example:

 #!perl
 use strict;
 use warnings;
 use Perinci::CmdLine::Any;

 our %SPEC;
 $SPEC{hello} = {
     v => 1.1,
     summary => 'Say hello',
     args => {
         name => {
             summary => 'Name to say hello to',
         },
     },
 };
 sub hello {
     my %args = @_;
     [200, "OK", "Hello, $args{name}!"];
 }

 Perinci::CmdLine::Any->new(url => '/main/hello')->run;

When you run this:

 % ./hello --name Jimmy
 Hello, Jimmy!

If you run C<./hello --help>, the option is now mentioned as well in the help
message.

Unknown arguments will result in an error:

 % ./hello --gender m
 ERROR 400: Unknown option '--gender'

To specify that an argument is required, add C<req> property to the argument
specification with a true value:

     args => {
         name => {
             summary => 'Name to say hello to',
             req => 1,
         },
     },

So when you run the app:

 % ./hello
 ERROR 400: Missing required argument 'name'

To specify that an argument can also be specified via positional command-line
I<argument> instead of just command-line I<option>, add C<pos> property to the
argument specification:

     args => {
         name => {
             summary => 'Name to say hello to',
             req => 1,
             pos => 0,
         },
     },

So when you run the app you can specify:

 % ./hello --name Jimmy
 Hello, Jimmy!

as well as:

 % ./hello Jimmy
 Hello, Jimmy!

Extra arguments will also result in an error:

 % ./hello Jimmy Gideon
 ERROR 400: Extra argument 'Gideon'

=head2 Argument schema (and more on text output formats)

Following up from the previous example, here's another example with more
arguments. Also note that I use P::C since P::C::Lite doesn't do schema
validation.

 #!perl
 use 5.010;
 use strict;
 use warnings;
 use Perinci::CmdLine;

 our %SPEC;
 $SPEC{hello} = {
     v => 1.1,
     summary => 'Say hello',
     args => {
         name => {
             summary => 'Name(s) to say hello to',
             schema  => [array => {of => 'str', min_len=>1}],
             req     => 1,
             pos     => 0,
             greedy  => 1,
         },
         gender => {
             summary => 'The gender of the name(s)',
             schema  => [str => {in => ['m','f']}],
         },
     },
 };
 sub hello {
     my %args = @_;
     my $g = $args{gender};
     my @res;
     for my $name (@{ $args{name} // [] }) {
         push @res, join("",
             "Hello, ",
             (!$g ? "" : $g eq 'm' ? "Mr. " : "Mrs. "),
             $name, '!',
         );
     }
     [200, "OK", \@res];
 }
 Perinci::CmdLine->new(url => '/main/hello')->run;

If you run this program:

 % ./hello Jimmy Sion Habil
 % ./hello --name Jimmy --name Sion --name Habil
 Hello, Jimmy!
 Hello, Sion!
 Hello, Habil!

 % ./hello --name-json '["Jimmy","Sion","Habil"]' --gender m
 Hello, Mr. Jimmy!
 Hello, Mr. Sion!
 Hello, Mr. Habil!

Some things you might notice. First, there is a C<schema> property for each
argument. C<name> is specified as having a type of array of strings. To set this
argument from the CLI, you can either specify multiple times (e.g. C<--name
NAME1 --name NAME2 ...>) or specify using JSON (i.e. C<--name-json JSONSTR>).

Second, the C<name> argument specifies the C<greedy> property. This is used in
conjunction with the C<pos> property. It declares that the argument will gobble
up command-line arguments from C<pos> to the end. So you can also specify the
values of the C<name> argument with C<ARG1 ARG2 ...>.

Third, if you specify value that does not validate, an error will be produced.

 % ./hello --name-json '[]'
 ERROR 400: Invalid value for argument 'name': Length must be at least 1

 % ./hello --name Jimmy --name Sion --name Habil --gender x
 ERROR 400: Invalid value for argument 'gender': Must be one of ["m","f"]

See L<Data::Sah> for more about the schema syntax.

Fourth, you return the result as a data structure (an array) instead of directly
printing the result using C<print()> or C<say()>. This is done to make your
function more reusable outside the context of CLI. P::C will format your data
structure nicely using L<Data::Format::Pretty>. Your array will be printed as a
multicolumn ANSI table by default, on interactive mode. If you pipe the output
of your program, you will by default get a simpler text output. This can be
chosen explicitly using the C<--format> common option.

 % ./hello Jimmy Sion Habil --format text; # will output pretty or simple depending on whether interactive
 % ./hello Jimmy Sion Habil --format text-simple; # will still output simple table even when interactive
 % ./hello Jimmy Sion Habil --format text-pretty; # will still output pretty table even when piped

=head2 Short option aliases

To add short options, you can use the C<cmdline_aliases> property in the
argument specification:

         name => {
             ...
             cmdline_aliases => { n => {} },
         },
         gender => {
             ...
             cmdline_aliases => { g => {} },
         },

Now instead of:

 % ./hello --name Jimmy --name Sion --name Habil --gender m

you can also use:

 % ./hello -n Jimmy -n Sion -n Habil -g m

=head2 More on command-line option aliases

You are not limited to one alias, or one letter:

         gender => {
             ...
             cmdline_aliases => { g => {}, sex => {} },
         },

Now all these are equivalent:

 % ./hello ... --gender m
 % ./hello ... -g m
 % ./hello ... --sex m

Suppose you want to create an alias C<-m> to mean C<--gender m> and C<-f> to
mean C<--gender f> instead:

         gender => {
             ...
             cmdline_aliases => {
                 m => { schema=>'bool', code => sub {my $args=shift; $args->{gender} = 'm' } },
                 f => { schema=>'bool', code => sub {my $args=shift; $args->{gender} = 'f' } },
             },
         },

Now you can say:

 % ./hello Jimmy Sion -m
 Hello, Mr. Jimmy!
 Hello, Mr. Sion!

 % ./hello Nunung Misye -f
 Hello, Mrs. Nunung!
 Hello, Mrs. Misye!

=head2 Subcommands

=head2 Default subcommand

A default subcommand can be defined. This subcommand is selected without user
specifying it the first command-line argument. A real-world example of this is
from L<File::Trash::Undoable>. The B<trash-u> command is by default selecting
the C<trash> subcommand:

 % trash-u file1 file2

is equivalent to:

 % trash-u --cmd trash file1 file2

To select another subcommand other than C<trash>, an explicit option is
needed:

 % trash-u --list-contents ; # select the list_contents subcommand
 % trash-u --cmd empty     ; # select the empty subcommand

This is done via something like:

 Perinci::CmdLine->new(
     subcommands => {
         trash => { url=>... },
         empty => { url=>... },
         list_contents => { url=>... },
     },
     default_subcommand => 'trash',
 )->run;

=head2 Default subcommand (override via first command-line argument)

There is also a choice to specify a default subcommand which is overrideable via
first command-line argument. A real-world example of this is from
L<App::GitUtils>. If the B<gu> command is specified without any argument:

 % gu

then it is equivalent to:

 % gu info

but user can specify other subcommands:

 % gu post-commit

This is accomplished by setting:

 Perinci::CmdLine::Any->new(
     subcommands => {
         info        => {...},
         run_hooks   => {...},
         post_commit => {...},
         ...
     },
     default_subcommand => 'info',
     get_subcommand_from_arg => 2,
 )->run;

=head2 Dynamic list of subcommands

=head2 Getting input from STDIN

=head2 Getting input from file contents

=head2 Streaming output

=head2 Pager

=head2 Configuration

In the function-centric world of Perinci::CmdLine, configuration is just another
way to supply values to function arguments (before being potentially overriden
by command-line arguments). Configuration files are written in L<IOD> format,
which is basically "INI with extra features". By default, configuration files
are searched in C</etc> and then your home directory, with the name of
I<program_name> + C<.conf>. So if you have:

 # ~/prog.conf
 foo=1
 bar=2

and:

 # prog
 #!perl
 use Perinci::CmdLine::Any;
 $SPEC{prog} = {
     v => 1.1,
     args => {
         foo => {},
         bar => {},
     },
 };
 sub prog {
     my %args = @_;
     [200, "OK", "foo is $args{foo}, while bar is $args{bar}"];
 }
 Perinci::CmdLine::Any->new(url=>'/main/prog')->run;

When you run:

 % prog

you'll get:

 foo is 1, while bar is 2

Multiple configuration files will be merged, so if you have:

 # /etc/prog.conf
 foo=1
 bar=2

 # ~/prog.conf
 foo=10

you'll get:

 foo is 10, while bar is 2

=head2 Configuration profiles

Configuration file can store more than one set of arguments, through specially
named sections, called profiles:

 # ~/prog.conf
 foo=1
 bar=2

 [profile=p1]
 foo=21
 bar=22

 [profile=p2]
 foo=31
 bar=32

Running the program:

 % prog
 foo is 1, while bar is 2
 % prog --config-profile p1
 foo is 21, while bar is 22
 % prog --config-profile p2
 foo is 31, while bar is 32

=head2 Configuration with subcommands

=head2 Ignoring configuration files

If you don't want to use any configuration files, you can use:

 % prog --noconfig ...

=head2 Modifying common options

=head2 Customizing help message

=head2 Logging/debugging

=head2 Remote function

=head2 Shell tab completion

=head2 I18N

=head1 SEE ALSO

L<Perinci::CmdLine::Manual>

=head1 HOMEPAGE

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

=head1 SOURCE

Source repository is at L<https://github.com/perlancar/perl-Perinci-CmdLine-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-CmdLine-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 AUTHOR

perlancar <perlancar@cpan.org>

=head1 COPYRIGHT AND LICENSE

This software is copyright (c) 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
