NAME

Getopt::Pad::Type - Base class of option types, and how to write your own

SYNOPSIS

use v5.26;
use Object::Pad;
use Getopt::Pad;
use Getopt::Pad::Type;

class My::Type::Even :isa(Getopt::Pad::Type) {
    use constant NAMES => ['even'];

    method glSuffix() { return '=s' }

    method check($value) {
        return sprintf("'%s' is not an integer", $value) if $value !~ /\A-?[0-9]+\z/;
        return sprintf('%s is not an even number', $value) if $value % 2;
        return undef;
    }

    method coerce($value) { return $value + 0 }
}

Getopt::Pad::Type::registerType('My::Type::Even');

my $opt = GetOptions(
    options => {
        workers => { type => 'even', default => 2, help => 'Number of workers, in pairs' },
    },
);
$ tool --workers 8          # $opt->workers is 8
$ tool --workers 7
ERROR: option '--workers': 7 is not an even number

DESCRIPTION

A type decides how the value of an option or arg is checked and converted, and how it is presented in the help output and in shell completion. Every built-in type (see "TYPES" in Getopt::Pad) is a subclass of Getopt::Pad::Type, and so is every type you write yourself.

A type is an Object::Pad class. Getopt::Pad creates one instance per option or arg that uses the type, so the instance can hold settings of that option, such as the min and max of an int option.

To add a type:

  1. Write a class that inherits from Getopt::Pad::Type.

  2. Give it a NAMES constant and a glSuffix method, and override the optional methods you need (see "THE TYPE CONTRACT").

  3. Register it with "registerType" before the first GetOptions call that uses one of its names.

After that, every spec in the program can use the names in its type keys.

THE TYPE CONTRACT

NAMES

use constant NAMES => ['duration', 'dur'];

Required. A constant that returns an arrayref of the names the type answers to in the type key of an option or arg spec. Names are matched case-insensitively. A name that another class has already registered, built-in or not, cannot be taken over: "registerType" dies. So a custom type can never replace string or int by accident.

glSuffix

method glSuffix() { return '=s' }

Required. Tells the command line parser whether the option takes a value. It returns one of these strings:

''      no value (--name), like the flag type
'!'     no value, negatable (--name, --no-name), like the bool type
'+'     no value, counts how often it is given, like the counter type
'=s'    takes a value

Almost every custom type returns '=s'. It is the right choice for numbers too: return '=s' and check the value in "check", so that every invalid value produces a Getopt::Pad error message. Types that take no value can be used for options only, not for args, and not with multiple, hash or objectlist.

The suffix is the Getopt::Long option specification suffix. It is the only part of that notation a type deals with; the base class builds the rest.

check

method check($value) {
    return undef if $value =~ /\A[0-9a-f]+\z/;
    return sprintf("'%s' is not a hexadecimal number", $value);
}

Optional. Called with one value, from the command line, a config file or the spec's default. Returns undef when the value is acceptable, otherwise a short description of the problem, without the option name: Getopt::Pad adds it, producing messages like option '--color': 'fff0' is not a hexadecimal number. The default accepts every value.

check always receives a single defined value: lists and mappings are taken apart before, and missing values are reported before. Values from config files can be numbers, or booleans: for true and false, JSON files give JSON::PP::Boolean objects, which compare and stringify as 1 and 0, and YAML files give 1 and the empty string. A value that fails check is not passed to any other method.

coerce

method coerce($value) { return hex $value }

Optional. Called with a value that passed "check". Returns the value the reader should return. The default returns the value unchanged. The numeric types use it to turn strings into numbers.

The valid list and the lazyValid check of an option see the converted value. The Default line of the help output shows the converted default, too.

prepare

method prepare($value) {
    return undef if !defined $value || -e $value;
    return sprintf("cannot create '%s': %s", $value, $!) if !mkdir $value;
    return undef;
}

Optional. Called with the final, converted value of an option or arg (once per value for options with several values), and never with a value that is overridden, such as a default when the command line sets the option. Use it for side effects the value needs; the built-in file and dir types create missing paths here for createPathIfMissing.

Returns undef on success, or a short description of the problem without the option name, which becomes a user error. It can be called with undef when the option's default is undef. It is not called for --help and the other automatic options, or for shell completion requests. The default does nothing.

label

method label() { return 'Duration' }

Optional. A short tag that the help output shows in brackets at the end of the option's help text, such as [Duration]. Returns undef for no tag, which is the default. An option or arg spec can override it with typehint.

constraintNotes

method constraintNotes() { return ('at most 1 hour') }

Optional. A list of short notes that the help output shows in brackets before the help text, such as [at most 1 hour]. The built-in path types return has to exist or created if missing. The default is an empty list.

completes

method completes() { return 'files' }

Optional. Which completion of its own the shell should offer for a value of this type: 'files', 'dirs', or undef for none (the default). The file and dir types use it. The values of an option's valid list are completed in any case.

SPEC_KEYS

use constant SPEC_KEYS => ['maxSeconds'];

field $maxSeconds :param = undef;

Optional. A constant that returns an arrayref of extra keys that option and arg specs may use with this type. Getopt::Pad takes these keys out of the spec and passes the ones that are present to the type's constructor as named parameters. Declare a :param field with a default for each key. With any other type, the keys are unknown keys and a spec error. This is how min, max, mustExist and createPathIfMissing reach the built-in types.

checkSpecKeys

method checkSpecKeys :common (%keys) {
    return undef if !defined $keys{maxSeconds} || $keys{maxSeconds} =~ /\A[0-9]+\z/;
    return sprintf("maxSeconds must be a whole number, not '%s'", $keys{maxSeconds});
}

Optional. A class method (:common) that checks the "SPEC_KEYS" values of one option or arg before the type is constructed. It receives the keys that are present and returns undef when they are acceptable, otherwise a short description of the problem without the option name. The problem becomes a spec error: Getopt::Pad spec: option 'timeout': maxSeconds must be a whole number, not '1h' at .... The default accepts everything.

Methods you do not need to write

The base class derives takesValue and negatable from "glSuffix", and builds the complete parser specification in glSpec. Overriding them is rarely useful.

HOW A VALUE PASSES THROUGH A TYPE

For every value of an option, Getopt::Pad calls, in this order:

  1. check. On a problem, the value is rejected with the returned message.

  2. coerce.

  3. The option's valid list and lazyValid check, with the converted value (not part of the type).

  4. prepare, for the value that is finally used.

For args, steps 1, 2 and 4 apply. Defaults pass steps 1 to 3 when GetOptions builds the spec; a problem there is a spec error.

FUNCTIONS

registerType

Getopt::Pad::Type::registerType('My::Type::Duration');

Registers a type class under the names in its "NAMES" constant, for all specs in the program. The argument is the class name. If the class has no NAMES method yet, which usually means that its module is not loaded, its module file is loaded first (for example My/Type/Duration.pm from @INC). So a type in its own module file needs no separate use.

It dies when a name is already registered by another class, with Getopt::Pad: option type name 'NAME' is already registered by CLASS, and when the class has no NAMES constant. Registering the same class again does nothing. Registration must happen before the GetOptions call whose spec uses the names.

The function is not exported; call it with its full name.

EXAMPLES

A type with an extra spec key

This type accepts durations such as 90s, 5m, 2h or 1d, and its readers return seconds. The extra spec key maxSeconds sets an upper limit:

use v5.26;
use Object::Pad;
use Getopt::Pad;
use Getopt::Pad::Type;

class My::Type::Duration :isa(Getopt::Pad::Type) {
    use constant NAMES     => ['duration'];
    use constant SPEC_KEYS => ['maxSeconds'];

    my %secondsPer = (s => 1, m => 60, h => 3600, d => 86400);

    field $maxSeconds :param = undef;

    # Runs once per option, before the type is constructed.
    method checkSpecKeys :common (%keys) {
        return undef if !defined $keys{maxSeconds} || $keys{maxSeconds} =~ /\A[0-9]+\z/;
        return sprintf("maxSeconds must be a whole number, not '%s'", $keys{maxSeconds});
    }

    method glSuffix() { return '=s' }

    method check($value) {
        my ($amount, $unit) = $value =~ /\A([0-9]+)([smhd])\z/
            or return sprintf("'%s' is not a duration such as 90s, 5m, 2h or 1d", $value);
        my $seconds = $amount * $secondsPer{$unit};
        return undef if !defined $maxSeconds || $seconds <= $maxSeconds;
        return sprintf('%s is longer than %d seconds', $value, $maxSeconds);
    }

    method coerce($value) {
        my ($amount, $unit) = $value =~ /\A([0-9]+)([smhd])\z/;
        return $amount * $secondsPer{$unit};
    }

    method label() { return 'Duration' }

    method constraintNotes() {
        return defined $maxSeconds ? (sprintf('at most %d seconds', $maxSeconds)) : ();
    }
}

Getopt::Pad::Type::registerType('My::Type::Duration');

my $opt = GetOptions(
    options => {
        timeout => {
            type       => 'duration',
            default    => '30s',
            maxSeconds => 3600,
            help       => 'How long to wait',
        },
    },
);

say $opt->timeout;
$ sleeper
30
$ sleeper --timeout 5m
300
$ sleeper --timeout 2h
ERROR: option '--timeout': 2h is longer than 3600 seconds
$ sleeper --timeout soon
ERROR: option '--timeout': 'soon' is not a duration such as 90s, 5m, 2h or 1d

The help output shows the constraint note, the label and the converted default:

## Options
   --timeout <>                [at most 3600 seconds] How long to wait
                               [Duration]
                                   Default = 30

A spec with maxSeconds => '1h' fails with Getopt::Pad spec: option 'timeout': maxSeconds must be a whole number, not '1h', and one with default => '2h' with Getopt::Pad spec: option 'timeout': default value: 2h is longer than 3600 seconds.

A type with shell completion

This type accepts only Perl scripts and lets the shell complete file names for it:

class My::Type::Script :isa(Getopt::Pad::Type) {
    use constant NAMES => ['script'];

    method glSuffix()  { return '=s' }
    method completes() { return 'files' }
    method label()     { return 'Perl Script' }

    method check($value) {
        return undef if $value =~ /\.pl\z/;
        return sprintf("'%s' is not a .pl file", $value);
    }
}

Getopt::Pad::Type::registerType('My::Type::Script');

my $opt = GetOptions(
    options => {
        run => { type => 'script', help => 'The script to run' },
    },
);

With the completion script installed (see "SHELL COMPLETION" in Getopt::Pad), tool --run <TAB> offers the file names in the current directory, like the built-in file type does. A value that does not end in .pl is rejected with option '--run': 'notes.txt' is not a .pl file.

More examples

The distribution's examples/04-custom-type.pl is a runnable version of the even type from the "SYNOPSIS". The built-in types are small and readable examples as well; see "BUILT-IN TYPE CLASSES".

BUILT-IN TYPE CLASSES

Getopt::Pad::Type::Flag

flag. A switch without a value. Defined in this module.

Getopt::Pad::Type::Bool

bool, boolean, !. A switch that can be negated. A subclass of the flag type, defined in this module.

Getopt::Pad::Type::Counter

counter, count, +. Defined in this module.

Getopt::Pad::Type::String

string, str, s. Accepts any value. Defined in this module.

Getopt::Pad::Type::Int, Getopt::Pad::Type::Float

int and float, both subclasses of Getopt::Pad::Type::Number, which provides min and max.

Getopt::Pad::Type::File, Getopt::Pad::Type::Dir

file and dir, both subclasses of Getopt::Pad::Type::Path, which provides mustExist and createPathIfMissing.

Getopt::Pad::Type::Url

url, uri.

What each type accepts is described in "TYPES" in Getopt::Pad.

SEE ALSO

Getopt::Pad, Getopt::Pad::Config::Format (the other extension point), Object::Pad

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.