=begin pod :kind("Language") :subkind("Language") :category("tutorial")

=TITLE Command line interface

=SUBTITLE Creating your own CLI in Raku

X<|command line arguments>
=head1 Command line interface - an overview

The default command line interface of Raku scripts consists of three parts:

=head2 Parsing the command line parameters into a L<capture|/type/Capture>

This looks at the values in L<@*ARGS|/language/variables#index-entry-@*ARGS>,
interprets these according to some policy, and creates a L<Capture|/type/Capture>
object out of that. An alternative way of parsing may be provided by the developer
or installed using a module.

=head2 Calling a provided C<MAIN> subroutine using that capture

Standard L<multi dispatch|/language/functions#index-entry-declarator_multi-Multi-dispatch>
is used to call the C<MAIN> subroutine with the generated C<Capture> object.
This means that your C<MAIN> subroutine may be a C<multi sub>, each candidate
of which is responsible for some part of processing the given command line
arguments.

=head2 Creating / showing usage information if calling C<MAIN> failed

If multi dispatch failed, then the user of the script should be informed as
well as possible as to why it failed. By default, this is done by inspecting
the signature of each C<MAIN> candidate sub, and any associated Pod information.
The result is then shown to the user on STDERR (or on STDOUT if C<--help>
was specified). An alternative way of generating the usage information may
be provided by the developer or installed using a module.

X<|MAIN>
=head1 sub MAIN

The sub with the special name C<MAIN> will be executed after all relevant entry
phasers (C<BEGIN>, C<CHECK>, C<INIT>, C<PRE>, C<ENTER>) have been run and
the L<mainline|/language/glossary#index-entry-Mainline> of the script has been
executed. No error will occur if there is no C<MAIN> sub: your script will
then just have to do the work, such as argument parsing, in the mainline of
the script.

Any normal exit from the C<MAIN> sub will result in an exit code of C<0>,
indicating success. Any return value of the C<MAIN> sub will be ignored.
If an exception is thrown that is not handled inside the C<MAIN> sub, then the
exit code will be C<1>. If the dispatch to C<MAIN> failed, a usage message
will be displayed on STDERR and the exit code will be C<2>.

The command line parameters are present in the C<@*ARGS> dynamic variable
and may be altered in the mainline of the script before the C<MAIN> unit is
called.

The signature of (the candidates of the multi) sub C<MAIN> determines which
candidate will actually be called using the standard
L<multi dispatch|/language/glossary#index-entry-Multi-Dispatch> semantics.

A simple example:

    # inside file 'hello.raku'
    sub MAIN($name) {
        say "Hello $name, how are you?"
    }

If you call that script without any parameters, you get the following
usage message:

=begin code :lang<shell>
$ raku hello.raku
Usage:
  hello.raku <name>
=end code

However, if you give a default value for the parameter, running the script
either with or without specifying a name will always work:

    # inside file 'hello.raku'
    sub MAIN($name = 'bashful') {
        say "Hello $name, how are you?"
    }

=begin code :lang<shell>
$ raku hello.raku
Hello bashful, how are you?
=end code

=begin code :lang<shell>
$ raku hello.raku Liz
Hello Liz, how are you?
=end code

Another way to do this is to make C<sub MAIN> a C<multi sub>:

    # inside file 'hello.raku'
    multi sub MAIN()      { say "Hello bashful, how are you?" }
    multi sub MAIN($name) { say "Hello $name, how are you?"   }

Which would give the same output as the examples above. Whether you should
use either method to achieve the desired goal is entirely up to you.

A more complicated example using a single positional and multiple
named parameters, and also showing that C<where> clauses can also be applied
to C<MAIN> arguments:

=for code :method<False>
# inside "frobnicate.raku"
sub MAIN(
  Str   $file where *.IO.f = 'file.dat',
  Int  :$length = 24,
  Bool :$verbose
) {
    say $length if $length.defined;
    say $file   if $file.defined;
    say 'Verbosity ', ($verbose ?? 'on' !! 'off');
}

With C<file.dat> present, this will work this way:
=begin code :lang<shell>
$ raku frobnicate.raku
24
file.dat
Verbosity off
=end code

Or this way with C<--verbose>:

=begin code :lang<shell>
$ raku frobnicate.raku --verbose
24
file.dat
Verbosity on
=end code

If the file C<file.dat> is not present, or you've specified another filename
that doesn't exist, you would get the standard usage message created from
introspection of the C<MAIN> sub:

=begin code :lang<shell>
$ raku frobnicate.raku doesnotexist.dat
Usage:
  frobnicate.raku [--length=<Int>] [--verbose] [<file>]
=end code

Although you don't have to do anything in your code to do this, it may still
be regarded as a bit terse. But there's an easy way to make that usage
message better by providing hints using pod features:

=for code :method<False>
# inside "frobnicate.raku"
sub MAIN(
  Str   $file where *.IO.f = 'file.dat',  #= an existing file to frobnicate
  Int  :$length = 24,                     #= length needed for frobnication
  Bool :$verbose,                         #= required verbosity
) {
    say $length if $length.defined;
    say $file   if $file.defined;
    say 'Verbosity ', ($verbose ?? 'on' !! 'off');
}

Which would improve the usage message like this:

=begin code :lang<shell>
$ raku frobnicate.raku doesnotexist.dat
Usage:
  frobnicate.raku [--length=<Int>] [--verbose] [<file>]

    [<file>]          an existing file to frobnicate
    --length=<Int>    length needed for frobnication
    --verbose         required verbosity
=end code

As any other subroutine, C<MAIN> can define
L<aliases|/type/Signature#index-entry-argument_aliases> for its named parameters.

=for code :method<False>
sub MAIN(
  Str   $file where *.IO.f = 'file.dat',  #= an existing file to frobnicate
  Int  :size(:$length) = 24,              #= length/size needed for frobnication
  Bool :$verbose,                         #= required verbosity
) {
    say $length if $length.defined;
    say $file   if $file.defined;
    say 'Verbosity ', ($verbose ?? 'on' !! 'off');
}

In which case, these aliases will also be listed as alternatives with C<--help>:

=begin code :lang<text>
Usage:
  frobnicate.raku [--size|--length=<Int>] [--verbose] [<file>]

    [<file>]                 an existing file to frobnicate
    --size|--length=<Int>    length needed for frobnication
    --verbose                required verbosity
=end code

C<Enumeration>s can be used in signatures with arguments converted
automatically to its corresponding C<enum> symbol:

=begin code
enum Flag  (
    FLAG_FOO => 0b001,
    FLAG_BAR => 0b010,
    FLAG_BAZ => 0b100,
);

sub MAIN(Flag $flag = FLAG_FOO) {
    say "Flagging $flag";
}
=end code

This will work correctly with

=for code :lang<text>
raku MAIN-enum.raku FLAG_BAR

but will die if called with something that is not a C<Flag>.

X<|%*SUB-MAIN-OPTS>
=head2 C<%*SUB-MAIN-OPTS>

It's possible to alter how arguments are processed before they're passed
to C<sub MAIN {}> by setting options in the C<%*SUB-MAIN-OPTS> hash. Due to
the nature of dynamic variables, it is required to set up the
C<%*SUB-MAIN-OPTS> hash and fill it with the appropriate settings.
For instance:

    my %*SUB-MAIN-OPTS =
      :named-anywhere,    # allow named variables at any location
      # other possible future options / custom options
    ;
    sub MAIN ($a, $b, :$c, :$d) {
        say "Accepted!"
    }

Available options are:

X<|named-anywhere>
=head3 C<named-anywhere>

By default, named arguments passed to the program (i.e., C<MAIN>)
cannot appear after any positional argument. However, if
C«%*SUB-MAIN-OPTS<named-anywhere>» is set to a true value, named arguments
can be specified anywhere, even after positional parameter. For example,
the above program can be called with:

=begin code :lang<shell>
$ raku example.raku 1 --c=2 3 --d=4
=end code

X<|command-line argument bundling>
=head3 C<bundling>

When C«%*SUB-MAIN-OPTS<bundling>» is set to a true value, single letter named
arguments can be bundled together with a single dash. The following two
commands are then equivalent:

=begin code :lang<shell>
$ raku example.raku -a -b -c
$ raku example.raku -abc
=end code

Bundled arguments can be understood as flags, that can neither be negated, nor
assigned a value though:

=begin code :lang<shell>
$ raku example.raku -/a       # OK
$ raku example.raku -a=asdf   # OK
$ raku example.raku -abc=asdf # Error
$ raku example.raku -/abc     # Error
=end code

This option is only available starting in the 2020.10 release.

X<|hidden-from-USAGE>
=head2 C<is hidden-from-USAGE>

Sometimes you want to exclude a C<MAIN> candidate from being shown in any
automatically generated usage message. This can be achieved by adding
a C<hidden-from-USAGE> trait to the specification of the C<MAIN> candidate
you do not want to show. Expanding on an earlier example:

    # inside file 'hello.raku'
    multi sub MAIN() is hidden-from-USAGE {
        say "Hello bashful, how are you?"
    }
    multi sub MAIN($name) {  #= the name by which you would like to be called
        say "Hello $name, how are you?"
    }

So, if you would call this script with just a named variable, you would get
the following usage:

=begin code :lang<shell>
$ raku hello.raku --verbose
Usage:
  hello.raku <name> -- the name by which you would like to be called
=end code

Without the C<hidden-from-USAGE> trait on the first candidate, it would have
looked like this:

=begin code :lang<shell>
$ raku hello.raku --verbose
Usage:
  hello.raku
  hello.raku <name> -- the name by which you would like to be called
=end code

Which, although technically correct, doesn't read as well.

X<|declarator,unit (MAIN)>
=head1 Unit-scoped definition of C<MAIN>

If the entire program body resides within C<MAIN>, you can use the C<unit>
declarator as follows (adapting an earlier example):

=begin code :solo
unit sub MAIN(
  Str   $file where *.IO.f = 'file.dat',
  Int  :$length = 24,
  Bool :$verbose,
);  # <- note semicolon here

say $length if $length.defined;
say $file   if $file.defined;
say 'Verbosity ', ($verbose ?? 'on' !! 'off');
# rest of script is part of MAIN
=end code

Note that this is only appropriate if you can get by with just a single
(only) C<sub MAIN>.

X<|USAGE>X<|$*USAGE>
=head2 sub USAGE

If no multi candidate of C<MAIN> is found for the given command line
parameters, the sub C<USAGE> is called. If no such method is found,
the compiler will output a default usage message.

    #|(is it the answer)
    multi MAIN(Int $i) { say $i == 42 ?? 'answer' !! 'dunno' }
    #|(divide two numbers)
    multi MAIN($a, $b){ say $a/$b }

    sub USAGE() {
        print Q:c:to/EOH/;
        Usage: {$*PROGRAM-NAME} [number]

        Prints the answer or 'dunno'.
    EOH
    }

The default usage message is available inside C<sub USAGE> via the read-only
C<$*USAGE> variable. It will be generated based on available C<sub MAIN>
candidates and their parameters. As shown before, you can specify an
additional extended description for each candidate using a
C<#|(...)> Pod block to set L«C<WHY>|/routine/WHY».

=head1 Intercepting CLI argument parsing (2018.10, v6.d and later)

You can replace or augment the default way of argument parsing by supplying a
C<ARGS-TO-CAPTURE> subroutine yourself, or by importing one from any of
the L<Getopt|https://modules.raku.org/search/?q=getopt> modules available
in the ecosystem.

X<|ARGS-TO-CAPTURE>
=head2 sub ARGS-TO-CAPTURE

The C<ARGS-TO-CAPTURE> subroutine should accept two parameters: a
L<Callable|/type/Callable> representing the C<MAIN> unit to be executed (so it
can be introspected if necessary) and an array with the arguments from the
command line. It should return a L<Capture|/type/Capture> object that will be
used to dispatch the C<MAIN> unit. The following is a B<very> contrived example
that will create a C<Capture> depending on some keyword that was entered (which
can be handy during testing of a command line interface of a script):

    sub ARGS-TO-CAPTURE(&main, @args --> Capture) {
        # if we only specified "frobnicate" as an argument
        @args == 1 && @args[0] eq 'frobnicate'
          # then dispatch as MAIN("foo","bar",verbose => 2)
          ?? Capture.new( list => <foo bar>, hash => { verbose => 2 } )
          # otherwise, use default processing of args
          !! &*ARGS-TO-CAPTURE(&main, @args)
    }

Note that the dynamic variable
L<C<&*ARGS-TO-CAPTURE>|/language/variables#&*ARGS-TO-CAPTURE> is available to
perform the default command line arguments to C<Capture> processing so you don't
have to reinvent the whole wheel if you don't want to.

=head1 Intercepting usage message generation (2018.10, v6.d and later)

You can replace or augment the default way of usage message generation
(after a failed dispatch to MAIN) by supplying a C<GENERATE-USAGE> subroutine
yourself, or by importing one from any of the
L<Getopt|https://modules.raku.org/search/?q=getopt> modules available in the
ecosystem.

X<|RUN-MAIN>
=head2 sub RUN-MAIN

Defined as:

    sub RUN-MAIN(&main, $mainline, :$in-as-argsfiles)

This routine allows complete control over the handling of C<MAIN>. It gets a
C<Callable> that is the C<MAIN> that should be executed, the return value of the
mainline execution and additional named variables: C<:in-as-argsfiles> which
will be C<True> if STDIN should be treated as C<$*ARGFILES>.

If C<RUN-MAIN> is not provided, a default one will be run that looks for
subroutines of the old interface, such as C<MAIN_HELPER> and C<USAGE>. If
found, it will execute following the "old" semantics.

=begin code
class Hero {
    has @!inventory;
    has Str $.name;
    submethod BUILD( :$name, :@inventory ) {
        $!name = $name;
        @!inventory = @inventory
    }
}

sub new-main($name, *@stuff ) {
    Hero.new(:name($name), :inventory(@stuff) ).raku.say
}

RUN-MAIN( &new-main, Nil );
=end code

This will print the name (first argument) of the generated object.


X<|GENERATE-USAGE>
=head2 sub GENERATE-USAGE

The C<GENERATE-USAGE> subroutine should accept a C<Callable> representing the
C<MAIN> subroutine that didn't get executed because the dispatch failed.
This can be used for introspection. All the other parameters are the
parameters that were set up to be sent to C<MAIN>. It should return the
string of the usage information you want to be shown to the user. An example
that will just recreate the C<Capture> that was created from processing the
arguments:

    sub GENERATE-USAGE(&main, |capture) {
        capture<foo>:exists
          ?? "You're not allowed to specify a --foo"
          !! &*GENERATE-USAGE(&main, |capture)
    }

You can also use multi subroutines to create the same effect:

    multi sub GENERATE-USAGE(&main, :$foo!) {
        "You're not allowed to specify a --foo"
    }
    multi sub GENERATE-USAGE(&main, |capture) {
        &*GENERATE-USAGE(&main, |capture)
    }

Note that the dynamic variable
L<C<&*GENERATE-USAGE>|/language/variables#&*GENERATE-USAGE> is available to
perform the default usage message generation so you don't have to reinvent the
whole wheel if you don't want to.

=head1 Intercepting MAIN calling (before 2018.10, v6.e)

An older interface enabled one to intercept the calling to C<MAIN> completely.
This depended on the existence of a C<MAIN_HELPER> subroutine that would be
called if a C<MAIN> subroutine was found in the mainline of a program.

This interface was never documented. However, any programs using this
undocumented interface will continue to function until C<v6.e>. From v6.d
onward, the use of the undocumented API will cause a C<DEPRECATED> message.

Ecosystem modules can provide both the new and the old interface for
compatibility with older versions of Perl 6 and Raku: if a newer Raku recognizes
the new (documented) interface, it will use that. If there is no new
interface subroutine available, but the old C<MAIN_HELPER> interface is,
then it will use the old interface.

If a module developer decides to only offer a module for C<v6.d> or higher,
then the support for the old interface can be removed from the module.

=end pod

# vim: expandtab softtabstop=4 shiftwidth=4 ft=perl6
