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
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.