NAME
Getopt::Pad - Object::Pad based POSIX compatible options processor
SYNOPSIS
use Getopt::Pad;
my $opt = GetOptions(
options => {
'owner|o' => {
type => 's',
required => 1,
help => 'Target owner',
},
},
args => [
{ type => 'url', short => 'source-url', required => 1, help => 'Source URL' },
],
description => 'Migrate one Git repository into Forgejo.',
);
say $opt->owner;
say $opt->sourceUrl;
Called with --help, this script prints:
# migrate [options] source-url
# Migrate one Git repository into Forgejo.
## Arguments
<source-url> [REQ] Source URL [URL]
## Completion
--create-completions <> Print a completion script for this shell to
STDOUT and exit
Valid = [ bash, zsh ]
## Options
--owner <> [REQ] Target owner
DESCRIPTION
Getopt::Pad parses command line options declared as a typed spec, validates them, and returns a runtime-generated Object::Pad object with one camelCase reader per option and positional argument. Parsing itself is delegated to Getopt::Long, configured with bundling, no_ignore_case and no_auto_abbrev (no long-option abbreviation).
Any parse or validation failure prints the specific error to STDERR, followed by the help text of the command level the error occurred on, and exits with status 2. Mistakes in the spec itself are programmer errors and die instead, with a message pointing at the GetOptions call rather than into the library. --help prints the generated usage text to STDOUT and exits 0.
FUNCTIONS
GetOptions(%spec)
Parses @ARGV (or the argv arrayref, see below) against the spec and returns a result object. Recognized top-level keys:
- options => \%options
-
Maps option keys to option specs. The key is the option's name, optionally with pipe-separated aliases (
'owner|o'). The first name defines the camelCase reader (work-dirbecomesworkDir). Each option spec accepts:type- a type name, see "TYPES". Defaults to a plain flag.required- the option must be given (mutually exclusive withdefault).default- the value used when the option is absent. It is validated like user input when the spec is built.valid- the allowed values: an arrayref, or a coderef returning such an arrayref when called (with no arguments) - for lists that are only known at run time, such as ids read from a database. The coderef runs when a value has to be checked and when the shell asks for completions (see "SHELL COMPLETION"). The help output lists only a static arrayref.lazyValid- a coderef called with one value, returning true when it is acceptable. Use it for constraints that cannot be listed. It runs aftervalid.group- the help section this option is rendered under (defaultOptions).help- the help text.multiple- the option may be repeated. The reader returns an arrayref.hidden- accept the option but leave it out of the help output.
Type-specific keys are accepted alongside:
mustExist(file, dir),min/max(int, float). - args => \@args
-
Positional arguments, consumed in order. Each entry accepts
short(the name, mandatory, defines the reader),type(defaultstring),required,help, and - on the last entry only -multipleto slurp all remaining positionals into an arrayref. Required args must precede optional ones. - commands => \%commands
-
Nested subcommands: each value is a hashref with the same keys as the top-level spec (except
config,versionandargv). The first bare word on the command line selects a command. Options before it belong to the outer level, everything after it to the inner level. A level may declareargsorcommands, never both.When a level has commands, naming one is mandatory unless the level sets
commandRequired => 0. The result'scommandandsubcommandreaders then return undef. - description => $text
-
One-line description shown in the help header.
- examples => \@examples
-
Hashrefs with
textandargs, rendered in the help's Examples section. - config => \%config
-
Enables config file support and the automatic
--configoption, which is listed in the help output under aConfiggroup (unlike--helpand--version, which stay hidden). Keys:format(mandatory,yamlorjson, see "EXTENDING"),paths(arrayref, loaded in order whenautoloadis on - later files override earlier ones),defaultPath(loaded by a bare--config),autoload(default 1). An explicit--config PATHreplaces the autoload chain. Precedence is always: command line over config file over spec default. Config files can only set top-level options. A subcommand's options cannot come from a config file.~in paths expands to$HOME.A bare
--configtakes the next word as its path unless that word starts with a dash, so it also swallows a following positional or command name. Write--config=to loaddefaultPathin that position.Config files are structured by group: each top-level key is a group name (as used in the option specs, with ungrouped options under
Options) containing a mapping of option names to values:Target: owner: dave Options: log-level: debugConfig values run through the same checks as command line values. A value without content (YAML
~or an empty entry, JSONnull) is an error, as is a list or mapping for an option withoutmultiple. Amultipleoption takes a list or a single value. Flag and bool options accept onlytrue/false, 1 and 0, counters only non-negative integers.A
--create-default-config PATHoption is added as well: it writes a config file prefilled with the spec's default values to PATH, refusing to overwrite an existing file, and exits 0. - version => $string
-
Printed by the automatic
--versionoption. It defaults to the calling script's$main::VERSION. - argv => \@words
-
Parse these words instead of
@ARGV.@ARGVitself is never modified.
Every level gets an automatic --help option and the root level an automatic --version and --create-completions SHELL option, the latter listed under a Completion group. See "SHELL COMPLETION".
RESULT OBJECT
A successful parse returns an instance of a freshly generated Object::Pad class with one :reader per option and arg, plus:
command- the selected subcommand name (or undef)subcommand- the nested result object (or undef)help- prints the usage text and exits 0version- prints the version and exits 0
An option that was never given (no command line value, no config value, no default) reads as undef. A multiple option or slurpy arg that was given reads as an arrayref.
Option and arg names whose reader would collide with something every result object already answers to are rejected when the spec is built: its methods (command, subcommand, help, version, new and what Object::Pad and UNIVERSAL provide, such as can or BUILDARGS), its constructor params, and the names Perl calls by itself (DESTROY, AUTOLOAD). The result class answers that question from its own method table, so the list is never copied. Duplicate names or aliases across the options of one level are rejected the same way.
SHELL COMPLETION
--create-completions bash or --create-completions zsh prints a completion script for the program to STDOUT and exits 0. Redirect it to wherever your shell picks completions up from, or source it:
tool --create-completions bash > ~/.local/share/bash-completion/completions/tool
tool --create-completions zsh > ~/.zsh/completions/_tool
The script is a thin shim: every time the user presses tab, it runs the program again with the environment variable GETOPT_PAD_COMPLETE set to the shell name, GETOPT_PAD_COMPLETE_INDEX set to the index of the word under the cursor, and the words typed so far as arguments. GetOptions notices the variable, prints the candidates and exits 0 instead of parsing, so nothing after the GetOptions call runs, and the script never needs to be regenerated when the spec changes. Anything the program does before it calls GetOptions runs on every tab, so keep the call early.
Completed are the command names of the current level, the option spellings the help lists (hidden options are left out, negatable ones also as --no-name), the values an option's valid list allows - a coderef is called on every tab, so ids fetched from a database complete to the ids that exist right now - and, for options and positional args of the file and dir types, the shell's own path completion.
TYPES
Types validate and coerce values and annotate the help output.
! bool boolean negatable flag (--name / --no-name)
+ counter count counting flag (-vvv)
s string str plain string
i int integer integer; min/max
f float num number number; min/max
file file path; mustExist
dir directory directory path; mustExist
url uri URL of the form scheme://...
flag plain non-negatable flag (the default)
EXTENDING
Both extension seams follow the same pattern: subclass an abstract base class, name the extension through a NAMES constant, and register the class. Runnable versions of the examples below live in the distribution's examples/ directory.
Custom option types
A type subclasses Getopt::Pad::Type and is registered with Getopt::Pad::Type::registerType($class). Every name it lists must be free: a name another type already holds (built-in or custom) makes the registration die, so nothing can silently replace s or int. The contract:
- NAMES (constant, required)
-
Arrayref of the names this type answers to in an option's
typekey, matched case insensitively (e.g.['i', 'int', 'integer']). - glSuffix (method, required)
-
The Getopt::Long suffix ("gl" is short for Getopt::Long) appended to the option names when the Getopt::Long option specification is built - for an option declared as
'owner|o'with a'=s'suffix, Getopt::Long is handedowner|o=s. Valid return values:'' plain flag, no value (--name) '!' negatable flag (--name / --no-name) '+' counting flag (-vvv) '=s' the option takes a valueValue-taking types should return
'=s'even for numbers - never'=i'or'=f'- and do their own checking incheck, so every invalid value produces a Getopt::Pad error message instead of a Getopt::Long one. The built-in Int and Float types work exactly this way. The suffix is the only Getopt::Long spelling a type contributes: the base class derivestakesValueandnegatablefrom it and assembles the full option specification inglSpec. Overriding those is rarely useful. - check($value) (method, optional)
-
Return
undefwhen the value is acceptable, otherwise a short problem description without the option name - the caller prefixes it with the option or argument the value came from. Runs for command line, config file and default values alike. Lists, mappings and null config values are rejected beforecheckis called, so a config value always arrives as a single scalar (or a JSON boolean object). - coerce($value) (method, optional)
-
Return the value to store after a successful check. The default returns it unchanged. Numeric types use this to turn the string into a number.
- label (method, optional)
-
A short tag rendered at the end of the help text, e.g.
URLrenders as[URL]. Returnundef(the default) for none. - constraintNotes (method, optional)
-
A list of annotations rendered before the help text, e.g.
'has to exist'renders as[has to exist]. The list is empty by default. - completes (method, optional)
-
Which of the shell's own completions a value of this type gets when the user presses tab:
'files','dirs', orundef(the default) for none. Thefileanddirtypes use it. - SPEC_KEYS (constant, optional)
-
Arrayref of extra option-spec keys this type consumes. The type module takes them out of the option or arg spec and passes them to the type's constructor as named parameters, and any key left over is reported as unknown. This is how
mustExist,minandmaxreach the built-in types. - checkSpecKeys(%keys) (class method, optional)
-
Checks the SPEC_KEYS values before the type is constructed. Return
undefwhen they are acceptable, otherwise a short problem description without the option name. It is reported as a spec error naming the option or arg. The numeric types use this to reject aminthat is not a number or larger thanmax.
A complete type accepting only even integers:
use Object::Pad;
use Getopt::Pad::Type;
class My::Type::Even :isa(Getopt::Pad::Type) {
use constant NAMES => ['even'];
method glSuffix() { return '=s' }
method coerce($value) { return $value + 0 }
method check($value) {
return "'$value' is not an integer" if $value !~ /^-?\d+$/;
return "$value is not an even number" if $value % 2;
return undef;
}
}
Getopt::Pad::Type::registerType('My::Type::Even');
# afterwards, in any spec:
options => { workers => { type => 'even' } }
Custom config formats
A format subclasses Getopt::Pad::Config::Format and is registered with Getopt::Pad::Config::Format::registerFormat($class). It provides a NAMES constant (like types, and with the same rule that a name held by another format cannot be taken over) and one method: parse($text), which turns the file's contents into a hashref of group names, each holding a hashref of option names to values. Getopt::Pad reads and writes the files itself as UTF-8: parse receives the decoded text as a Perl character string and never opens a file, and encoding is not the format's concern. On parse problems it should simply die. The message is reported to the user as a config error naming the file. A format may additionally provide dump($data), returning such a structure serialized as text (again a character string, not UTF-8 octets) - without it, --create-default-config refuses to write files in that format. A TOML format via TOML::Tiny:
use Object::Pad;
use Getopt::Pad::Config::Format;
class My::Format::Toml :isa(Getopt::Pad::Config::Format) {
use Carp qw(croak);
use Feature::Compat::Try;
use constant NAMES => ['toml'];
method parse($text) {
try { require TOML::Tiny }
catch ($error) { croak "config format 'toml' requires the TOML::Tiny module" }
return TOML::Tiny::from_toml($text);
}
}
Getopt::Pad::Config::Format::registerFormat('My::Format::Toml');
# afterwards, in any spec:
config => { format => 'toml', paths => ['~/.mytool.toml'] }
EXAMPLES
The distribution ships runnable examples in examples/. Each one parses your arguments (its header comment lists invocations to try) and prints an overview of the readers on the result object, indenting nested subcommand results:
01-basic.pl - options, defaults, valid lists, positionals
02-types.pl - every built-in option type
03-commands.pl - nested subcommands and chained results
04-custom-type.pl - registering the
eventype shown above05-custom-format.pl - registering the TOML format shown above
SEE ALSO
AUTHOR
davenonymous <perl@davenonymous.com>
COPYRIGHT AND LICENSE
Copyright 2026 davenonymous
This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.