sciurius / perl-Getopt-Long

Module Getopt::Long - extended processing of command line options
13 stars 17 forks source link

RFE: allow '.' in option names #16

Closed djerius closed 11 months ago

djerius commented 1 year ago

I would like to use the . character in option names to indicate levels in a hierarchical configuration. I can then run the resultant hash through, e.g. Hash::Fold::unfold, and merge it with nested hashes read from configuration files.

Unfortunately, . is not allowed in option names:

% perl -MGetopt::Long -E 'GetOptions( q{my_nested_option} )'
% perl -MGetopt::Long -E 'GetOptions( q{my.nested.option} )'
Error in option spec: "my.nested.option"

Using another character, such as _, or -, invites confusion, as they are already in common use in option names.

sciurius commented 1 year ago

I usually do this with a hash tied to an option, e.g.

GetOptions( 'option|O=s' => \%hash, ... );

--option foo.bar.blech=1 -O foo.blech.bar=0
djerius commented 1 year ago

I usually do this with a hash tied to an option, e.g.

GetOptions( 'option|O=s' => \%hash, ... );

--option foo.bar.blech=1 -O foo.blech.bar=0

That requires further validation to check that it's a legal option key (I'm lazy, I'd like GetOptions to do the work).

Also, for the user it presents an inconsistent interface. For example, why are some boolean parameters specified as

  --foo

while others,

  --option bar.baz=1

instead of

--bar.baz
sciurius commented 1 year ago

I'm lazy, I'd like GetOptions to do the work

Actually, that sounds like "I'm lazy, I want you to do the work" :).

Anyway, allowing periods in options is a sensible suggestion so I'll take it into consideration.

djerius commented 1 year ago

I'm lazy, I'd like GetOptions to do the work

Actually, that sounds like "I'm lazy, I want you to do the work" :).

While I won't say that wouldn't be appreciated, I'm willing to submit a patch.

sciurius commented 1 year ago

Actually I was wondering whether GetOptions could do something more than just allowing foo.bar as an option. E.g. if you specify

'foo.bar=s' => \$foo

with --foo.bar=blech that would yield $foo = { bar => 'blech' } instead of just $foo = 'blech'.

Ideas?

djerius commented 1 year ago

Actually I was wondering whether GetOptions could do something more than just allowing foo.bar as an option. E.g. if you specify

'foo.bar=s' => \$foo

with --foo.bar=blech that would yield $foo = { bar => 'blech' } instead of just $foo = 'blech'.

I think that strays into the regime of setting policy about the interpretation of the argument names. To you and I, . may be hierarchical, but to someone else it may be an alternative means of making variable names easier to read.

I'd prefer to fall back on a common understanding of how . is used in the larger (i.e., not Getopt::Long) world of command line parsing, but I'm not sure that exists. In any case, once . is an allowed character, the user can easily perform the unfolding of the argument name into a hash:

(this code uses _ as a proxy for .)

#! perl

use v5.10;
use strict;
use warnings;

use Getopt::Long;
use Data::Dumper;

my %options;

sub tohash {
    my ( $name, $value ) = @_;

    my @path = split /[_]/, $name;
    my $leaf = pop @path;

    my $href = \%options;
    $href = $options{$_} //= {} for @path;
    $href->{$leaf} = $value;
}

GetOptions( \%options, "foo_bar=s" => \&tohash, "foo_blech=s" => \&tohash );

say Dumper \%options;

Or if they don't mind dragging in something like Hash::Fold

#! perl

use v5.10;
use strict;
use warnings;

use Getopt::Long;
use Hash::Fold;
use Data::Dumper;

my %options;

GetOptions( \%options, "foo_bar=s", "foo_blech=s" );

say Dumper( Hash::Fold->new( delimiter => '_' )->unfold( \%options ) );
sciurius commented 1 year ago

Checked in. Please test.

djerius commented 1 year ago

Thanks. I've poked at it in various ways (rediscovering the no_auto_abbrev config option when looking at how trailing and sequential dots are handled) and it looks solid.

djerius commented 11 months ago

Thanks!