NAME

Fugu::CLI - subcommand dispatch for a command-line tool

SYNOPSIS

use Fugu::CLI;

my $cli = Fugu::CLI->new(
    name    => 'mytool',
    usage   => '[-c file] <command>',
    options => { 'config|c=s' => 'the configuration file' },
    commands => {
        status => {
            summary => 'report the state',
            run     => sub ($cli, @argv) {
                say 'running';
                return Fugu::CLI::EXIT_SUCCESS;
            },
        },
    },
);

exit $cli->run(@ARGV);

DESCRIPTION

Fugu::CLI dispatches the subcommands of a command-line tool. A tool declares its global options once and its commands in a table. The module parses the global options, finds the command, parses that command's own options, and calls it. Every command in a tool would otherwise repeat the same Getopt::Long setup.

Output has two channels, and they are not the same channel. Data that a script reads goes to standard output; a command prints it itself. Diagnostics go to the logger, which writes to standard error. Thus mytool list --names | xargs keeps working when the tool starts to warn about something.

new

new(%args) creates a dispatcher.

These are the arguments:

commands

The subcommand table, as a hash reference. This argument is necessary.

name

The program name in the usage line and in diagnostics. The default is cli.

options

The global options, as a hash reference whose keys are Getopt::Long specifications and whose values describe them.

usage

The one line that follows usage: <name>.

epilogue

Text that follows the command list in the help. Put examples here.

log

The logger. The default is Fugu::Log->default.

Each entry of commands maps a name to a hash reference:

run

The body, as a code reference. This key is necessary. The body receives the dispatcher and the remaining arguments, and returns an exit code.

usage

The argument summary of this command.

summary

One line for the command list.

options

This command's own options, in the same form as options.

run

run(@argv) parses, dispatches, and returns the exit code of the command.

An unknown command, a bad option, and a missing command all give EXIT_INVALID_ARGS with the usage line on standard error.

A command name of help, and an empty argument list, print the help and return EXIT_SUCCESS. Asking for help is not a failure. Every command takes --help, whether it declared the option or not.

option

option($name) returns a parsed option value. The name is the first name of the specification, so config|c=s arrives as config.

Global options and the running command's options share one namespace. A tool that gives the same name two meanings is a tool nobody can use.

options

options() returns every parsed option as a hash reference.

command

command() returns the name of the command that is running.

name

name() returns the program name.

log

log() returns the logger. A command body reports through it.

print_help($command) prints the help for one command, or for the whole tool. The help goes to standard output, because the user asked for it.

usage_error

usage_error() prints the usage line on standard error.

command_usage_error

command_usage_error($command) prints the usage line of one command on standard error.

EXIT CODES

The module defines the codes that every tool shares:

EXIT_SUCCESS

0

EXIT_ERROR

1

EXIT_INVALID_ARGS

2

EXIT_CONFIG_ERROR

3

EXIT_TIMEOUT

7

A caller can import EXIT_SUCCESS and EXIT_ERROR with use Fugu::CLI qw(EXIT_SUCCESS EXIT_ERROR).

A code that belongs to one tool is defined by that tool, not here, and starts above these.

RETURN VALUES

new() returns a dispatcher. run() returns an exit code. option() returns a value or undef. options() returns a hash reference. name() and command() return a string. log() returns a logger.

EXAMPLES

This example declares a command with its own option:

list => {
    summary => 'list the cached images',
    usage   => '[--names]',
    options => { 'names' => 'print names only' },
    run     => sub ($cli, @argv) {
        for my $entry (@entries) {
            say $cli->option('names')
                ? $entry->{name}
                : sprintf('%-20s %s', $entry->{name}, $entry->{size});
        }
        return Fugu::CLI::EXIT_SUCCESS;
    },
},

ERRORS

new() dies when the command table is absent, and when a command has no run code reference. Both are programming errors that a tool hits on its first run.

run() does not die. Every input failure gives EXIT_INVALID_ARGS and a usage line.

SEE ALSO

Fugu::Config, Fugu::Log, Getopt::Long

AUTHORS

Dick Olsson <hi@senzilla.io>

CAVEATS

The module does not catch a die from a command body. A body that must not kill the tool catches its own errors.

Global options must come before the command name, and a command's own options after it. The parser stops at the first argument that is not an option, which is how a command can take an option name that the tool also uses.