views:

141

answers:

3

I'm being forced (by engineering policy) to use Getopt::Euclid to parse arguments to my Perl program. I have three arguments, foo, bar and blah.

It is legitimate to have none of these and use other arguments instead.

If foo is present than exactly one of bar or blah should be present, and if either bar or blah are present, than foo must be present.

After reading the CPAN documentation, I don't see any way to get Euclid to detect and enforce these restrictions. If Euclid CAN enforce these restrictions, I'd like to understand how.

Otherwise, I'll detect the conditions myself, but would like to coax Euclid into generating the --help output if the conditions are violated, but can't see how to do that from inside the program either. Ideas?

+1  A: 

Well, it looks like you can use the OPTIONS section for your options and it won't complain if they are not there, but I don't see any logic to allow dependent options, so you would have to write that logic yourself. I also don't see any functions that will print out the usage, but you could always say

system $^X, $0, "--help";

which will run the script ($0) with the same interpreter ($^X) it was invoked with and pass it the argument --help. It is ugly, but it should work.

#!/usr/bin/perl

use strict;
use warnings;

use Getopt::Euclid;

sub usage {
    my $message = shift;
    print "$message\n\n";
    system $^X, $0, "--help";
}

if (keys %ARGV) {
    unless (exists $ARGV{'--foo'}) {
     usage "--foo must be present if --bar or --blah is present";
     exit -1;
    }

    if ($ARGV{'--bar'} and $ARGV{'--blah'}) {
     usage "only one of --bar or --blah may be present";
     exit -1;
    }
}

unless ($ARGV{'--foo'}) {
    print "Doing nothing\n";
    exit 0;
}

print "fooing bar\n"  if $ARGV{'--bar'};
print "fooing blah\n" if $ARGV{'--blah'};


__END__

=head1 NAME 

foo.pl - foo the bar 

=head1 VERSION

1.0

=head1 usage 

    foo.pl [options]

=head1 OPTIONS

=over

=item --foo

do the foo (requires --bar or --blah)

=item --bar

what to do the foo to (requires --foo)

=item --blah

what to do the foo to (requires --foo)

=item --help

=back

=head1 AUTHOR

Chas. J. Owens IV

=head1 BUGS

Hopefully none

=head1 COPYRIGHT

Copyright (c) 2009, Chas. J. Owens IV. All Rights Reserved.
This module is free software. It may be used, redistributed
and/or modified under the terms of the Perl Artistic License
(see http://www.perl.com/perl/misc/Artistic.html)
Chas. Owens
A: 

Looks to me (never used G::E and haven't tested) like you want to pretend it's just one option:

=head1 OPTIONS

=over

=item --foo <foo> --bar <bar> | --foo <foo> --baz <baz>

then use $ARGV{'--foo'}{'foo'} and either $ARGV{'--foo'}{'bar'} or $ARGV{'--foo'}{'baz'}.

I guess I'm assuming they all take arguments; you didn't make that clear.

Update: seems to work, though it gives misleading error messages if you omit bar or baz or specify both. If none of the switches take arguments, you can do:

=item --foo --bar | --foo --baz

=for Euclid
    false: --foo --baz

and $ARGV{'--foo'} will be true for bar, false for baz, and not existing if neither.

In the long run, you may be better off sending the Getopt::Euclid author a patch that allows something like:

=item --foo

=for Euclid
    requires: --bar | --baz

=item --bar

=for Euclid
    requires: --foo

=item --baz

=for Euclid
    requires: --foo

so it can produce meaningful error messages if inconsistent options are specified.

ysth
A: 

What about the features described in the "Placeholder constraints" section of Getopt::Euclid's manual? Specifically, this form:

PLACEHOLDER.type: TYPE [, EXPRESSION_INVOLVING(PLACEHOLDER)]

The EXPRESSION_INVOLVING can be any arbitrary perl expression, I believe. It doesn't even have to involve the option itself. So the constraint expressions for bar and blah can each check that the other one doesn't exist and that foo does exist.

The only problem is that this probably imposes an order dependence on your arguments. This would be legal: --foo --bar; but this wouldn't: --bar --foo, because when the --bar argument is read, Euclid doesn't know about --foo yet, so it croaks. So if you go this route, make sure to specify an error message to this effect for --bar.

Ryan Thompson