SYNOPSIS

    use Getopt::Panjang qw(get_options);

    my $opts;
    my $res = get_options(
        # similar to Getopt::Long, except values must be coderef (handler), and
        # handler receives hash argument
        spec => {
            'bar'   => sub { $opts->{bar} = 1 },
            'baz=s' => sub { my %a = @_; $opts->{baz} = $a{value} },
            'err=s' => sub { die "Bzzt\n" },
        },
        argv => ["--baz", 1, "--bar"], # defaults to @ARGV
    );

    if ($res->[0] == 200) {
        # do stuffs with parsed options, $opts
    } else {
        die $res->[1];
    }

   Sample success result when @ARGV is ["--baz", 1, "--bar"]:

    [200, "OK", undef, { "func.remaining_argv" => [] }]

   Sample error result (ambiguous option) when @ARGV is ["--ba", 1]:

    [
      500,
      "Ambiguous option 'ba' (bar/baz?)",
      undef,
      {
        "func.ambiguous_opts" => { ba => ["bar", "baz"] },
        "func.remaining_argv" => [1],
      },
    ]

   Sample error result (option with missing value) when @ARGV is ["--bar",
   "--baz"]:

   [ 500, "Missing required value for option 'baz'", undef, {
   "func.remaining_argv" => [], "func.val_missing_opts" => { baz => 1 },
   }, ]

   Sample error result (unknown option) when @ARGV is ["--foo", "--qux"]:

    [
       500,
      "Unknown options 'foo', 'qux'",
      undef,
      {
        "func.remaining_argv" => ["--foo", "--qux"],
        "func.unknown_opts"   => { foo => 1, qux => 1 },
      },
    ]

   Sample error result (option with invalid value where the option handler
   dies) when @ARGV is ["--err", 1]:

    [
      500,
      "Invalid value for option 'err': Invalid value for option 'err': Bzzt\n",
      undef,
      {
        "func.remaining_argv"   => [],
        "func.val_invalid_opts" => { err => "Invalid value for option 'err': Bzzt\n" },
      },
    ]

DESCRIPTION

   EXPERIMENTAL WORK.

   This module is similar to Getopt::Long, but with a rather different
   interface. After experimenting with Getopt::Long::Less and
   Getopt::Long::EvenLess (which offers interface compatibility with
   Getopt::Long), I'm now trying a different interface which will enable
   me to "clean up" or do "more advanced" stuffs.

   Here are the goals of Getopt::Panjang:

     * low startup overhead

     Less than Getopt::Long, comparable to Getopt::Long::EvenLess.

     * feature parity with Getopt::Long::EvenLess

     More features will be offered in the future.

     * more detailed error return

     This is the main goal which motivates me to write Getopt::Panjang. In
     Getopt::Long, if there is an error like an unknown option, or
     validation error for an option's value, or missing option value, you
     only get a string warning. Getopt::Panjang will instead return a data
     structure with more details so you can know which option is missing
     the value, which unknown option is specified by the user, etc. This
     will enable scripts/frameworks to do something about it, e.g. suggest
     the correct option when mistyped.

   The interface differences with Getopt::Long:

     * There is only a single function, and no default exports

     Getopt::Long has GetOptions, GetOptionsFromArray,
     GetOptionsFromString. We only offer get_options which must be
     exported explicitly.

     * capitalization of function names

     Lowercase with underscores (get_options) is used instead of camel
     case (GetOptions).

     * get_options accepts hash argument

     This future-proofs the function when we want to add more
     configuration.

     * option handler also accepts hash argument

     This future-proofs the handler when we want to give more arguments to
     the handler.

     * There are no globals

     Every configuration is specified through the get_options function.
     This is cleaner.

     * get_options never dies, never prints warnings

     It only returns the detailed error structure so you can do whatever
     about it.

     * get_options never modifies argv/@ARGV

     Remaining argv after parsing is returned in the result metadata.

   Sample startup overhead benchmark:

   # COMMAND: perl devscripts/bench-startup 2>&1

SEE ALSO

   Getopt::Long

   Getopt::Long::Less, Getopt::Long::EvenLess

   Perinci::Sub::Getopt