File Coverage

blib/lib/Getopt/Lucid.pm
Criterion Covered Total %
statement 481 483 99.5
branch 302 322 93.7
condition 42 50 84.0
subroutine 53 53 100.0
pod 16 16 100.0
total 894 924 96.7


line stmt bran cond sub pod time code
1 12     12   883061 use 5.008001;
  12         136  
2 12     12   59 use strict;
  12         22  
  12         251  
3 12     12   56 use warnings;
  12         20  
  12         1056  
4             package Getopt::Lucid;
5             # ABSTRACT: Clear, readable syntax for command line processing
6              
7             our $VERSION = '1.09';
8              
9             our @EXPORT_OK = qw(Switch Counter Param List Keypair);
10             our %EXPORT_TAGS = ( all => [ @EXPORT_OK ] );
11             our @ISA = qw( Exporter );
12              
13 12     12   89 use Carp;
  12         23  
  12         692  
14 12     12   78 use Exporter ();
  12         46  
  12         272  
15 12     12   74 use File::Basename ();
  12         37  
  12         209  
16 12     12   67 use List::Util ();
  12         44  
  12         328  
17 12     12   3821 use Getopt::Lucid::Exception;
  12         28  
  12         659  
18 12     12   6753 use Storable 2.16 qw(dclone);
  12         35937  
  12         20997  
19              
20             # Definitions
21             my $VALID_STARTCHAR = "a-zA-Z0-9";
22             my $VALID_CHAR = "a-zA-Z0-9_-";
23             my $VALID_LONG = qr/--[$VALID_STARTCHAR][$VALID_CHAR]*/;
24             my $VALID_SHORT = qr/-[$VALID_STARTCHAR]/;
25             my $VALID_BARE = qr/[$VALID_STARTCHAR][$VALID_CHAR]*/;
26             my $VALID_NAME = qr/$VALID_LONG|$VALID_SHORT|$VALID_BARE/;
27             my $SHORT_BUNDLE = qr/-[$VALID_STARTCHAR]{2,}/;
28             my $NEGATIVE = qr/(?:--)?no-/;
29              
30             my @valid_keys = qw( name type default nocase valid needs canon doc );
31             my @valid_types = qw( switch counter parameter list keypair);
32              
33             sub Switch {
34 49     49 1 8994 return bless { name => shift, type => 'switch' },
35             "Getopt::Lucid::Spec";
36             }
37             sub Counter {
38 24     24 1 4845 return bless { name => shift, type => 'counter' },
39             "Getopt::Lucid::Spec";
40             }
41             sub Param {
42 36     36 1 153 my $self = { name => shift, type => 'parameter' };
43 36 100       71 $self->{valid} = shift if @_;
44 36         182 return bless $self, "Getopt::Lucid::Spec";
45             }
46             sub List {
47 10     10 1 22 my $self = { name => shift, type => 'list' };
48 10 100       30 $self->{valid} = shift if @_;
49 10         51 return bless $self, "Getopt::Lucid::Spec";
50             }
51             sub Keypair {
52 13     13 1 74 my $self = { name => shift, type => 'keypair' };
53 13 100       33 $self->{valid} = [ @_ ] if scalar @_;
54 13         80 return bless $self, "Getopt::Lucid::Spec";
55             }
56              
57             package
58             Getopt::Lucid::Spec;
59             $Getopt::Lucid::Spec::VERSION = $Getopt::Lucid::VERSION;
60              
61             # alternate way to specify validation
62             sub valid {
63 2     2   3 my $self = shift;
64             Getopt::Lucid::throw_spec("valid() is not supported for '$self->{type}' options")
65 2 50       3 unless grep { $self->{type} eq $_ } qw/parameter list keypair/;
  6         12  
66 2 50       4 $self->{valid} = $self->{type} eq 'keypair' ? [ @_ ] : shift;
67 2         5 return $self;
68             }
69              
70             sub default {
71 37     37   48 my $self = shift;
72 37         53 my $type = $self->{type};
73 37 100       81 if ($self->{type} eq 'keypair') {
    100          
74 6 100       29 if (ref($_[0]) eq 'HASH') {
    100          
75 2         4 $self->{default} = shift;
76             }
77             elsif ( @_ % 2 == 0 ) {
78 3         8 $self->{default} = { @_ };
79             }
80             else {
81 1         2 $self->{default} = []; # will cause an exception later
82             }
83             }
84             elsif ( $self->{type} eq 'list' ) {
85 5         12 $self->{default} = [ @_ ];
86             }
87             else {
88 26         37 $self->{default} = shift;
89             }
90 37         230 return $self
91             };
92              
93 2     2   17 sub anycase { my $self = shift; $self->{nocase}=1; return $self };
  2         10  
  2         1464  
94              
95 5     5   7 sub needs { my $self = shift; $self->{needs}=[@_]; return $self };
  5         13  
  5         8  
96              
97 3     3   5 sub doc { my $self = shift; $self->{doc}=shift; return $self };
  3         11  
  3         11  
98              
99 541     541   554 sub _clone { my $self = shift; bless { %$self }, ref $self }
  541         1658  
100              
101             package Getopt::Lucid;
102              
103             #--------------------------------------------------------------------------#
104             # new()
105             #--------------------------------------------------------------------------#
106              
107             my @params = qw/strict target/;
108              
109             sub new {
110 190     190 1 21372 my ($class, $spec, $target) = @_;
111 190 100       449 my $args = ref($_[-1]) eq 'HASH' ? pop(@_) : {};
112 190 100       451 $args->{target} = ref($target) eq 'ARRAY' ? $target : \@ARGV;
113 190         249 my $self = {};
114 190         550 $self->{$_} = $args->{$_} for @params;
115 190         249 $self->{raw_spec} = $spec;
116 190 50       321 bless ($self, ref($class) ? ref($class) : $class);
117             throw_usage("Getopt::Lucid->new() requires an option specification array reference")
118 190 100       390 unless ref($self->{raw_spec}) eq 'ARRAY';
119 189         419 _parse_spec($self);
120 180         377 _set_defaults($self);
121 178         425 $self->{options} = {};
122 178         281 $self->{parsed} = [];
123 178         219 $self->{seen}{$_} = 0 for keys %{$self->{spec}};
  178         605  
124 178         588 return $self;
125             }
126              
127             #--------------------------------------------------------------------------#
128             # append_defaults()
129             #--------------------------------------------------------------------------#
130              
131             sub append_defaults {
132 8     8 1 4888 my $self = shift;
133             my %append =
134 8 100       37 ref $_[0] eq 'HASH' ? %{+shift} :
  4 100       19  
135             (@_ % 2 == 0) ? @_ :
136             throw_usage("Argument to append_defaults() must be a hash or hash reference");
137 7         11 for my $name ( keys %{$self->{spec}} ) {
  7         23  
138 43         85 my $spec = $self->{spec}{$name};
139 43         63 my $strip = $self->{strip}{$name};
140 43 100       76 next unless exists $append{$strip};
141 29         40 for ( $spec->{type} ) {
142 29 100       116 /switch|parameter/ && do {
143 15         22 $self->{default}{$strip} = $append{$strip};
144 15         17 last;
145             };
146 14 100       31 /counter/ && do {
147 4         7 $self->{default}{$strip} += $append{$strip};
148 4         4 last;
149             };
150 10 100       21 /list/ && do {
151             throw_usage("Option '$strip' in append_defaults() must be scalar or array reference")
152 5 100 100     28 if ref($append{$strip}) && ref($append{$strip}) ne 'ARRAY';
153             $append{$strip} = ref($append{$strip}) eq 'ARRAY'
154             ? dclone( $append{$strip} )
155 4 100       36 : [ $append{$strip} ] ;
156 4         7 push @{$self->{default}{$strip}}, @{$append{$strip}};
  4         7  
  4         7  
157 4         6 last;
158             };
159 5 50       13 /keypair/ && do {
160             throw_usage("Option '$strip' in append_defaults() must be scalar or hash reference")
161 5 100 66     31 if ref($append{$strip}) && ref($append{$strip}) ne 'HASH';
162             $self->{default}{$strip} = {
163 4         12 %{$self->{default}{$strip}},
164 4         6 %{$append{$strip}},
  4         14  
165             };
166 4         11 last;
167             };
168             }
169             throw_spec("Default '$spec->{canon}' = '$self->{default}{$strip}' fails to validate")
170 27 100       52 unless _validate_value($self, $self->{default}{$strip}, $spec->{valid} );
171             }
172 4         11 _recalculate_options($self);
173 4         10 return $self->options;
174             }
175              
176             #--------------------------------------------------------------------------#
177             # defaults()
178             #--------------------------------------------------------------------------#
179              
180             sub defaults {
181 7     7 1 8 my ($self) = @_;
182 7         8 return %{dclone($self->{default})};
  7         180  
183             }
184              
185              
186             #--------------------------------------------------------------------------#
187             # getopt()
188             #--------------------------------------------------------------------------#
189              
190             sub getopt {
191 141     141 1 3071 my ($self,$spec,$target) = @_;
192 141 100       296 if ( $self eq 'Getopt::Lucid' ) {
193 2 100       7 throw_usage("Getopt::Lucid->getopt() requires an option specification array reference")
194             unless ref($spec) eq 'ARRAY';
195 1         3 $self = new(@_)
196             }
197 140         141 my (@passthrough);
198 140         150 while (@{$self->{target}}) {
  375         637  
199 287         277 my $raw = shift @{$self->{target}};
  287         433  
200 287 100       567 last if $raw =~ /^--$/;
201 272         360 my ($orig, $val) = _split_equals($self, $raw);
202 272 100       455 next if _unbundle($self, $orig, $val);
203 248 100       756 my $neg = $orig =~ s/^$NEGATIVE(.*)$/$1/ ? 1 : 0;
204 248         378 my $arg = _find_arg($self, $orig);
205 248 100       351 if ( $arg ) {
206             $neg ?
207             $self->{seen}{$arg} = 0 :
208 232 100       338 $self->{seen}{$arg}++;
209 232         340 for ($self->{spec}{$arg}{type}) {
210 232 50       679 /switch/ ? _switch ($self, $arg, $val, $neg) :
    100          
    100          
    100          
    100          
211             /counter/ ? _counter ($self, $arg, $val, $neg) :
212             /parameter/ ? _parameter($self, $arg, $val, $neg) :
213             /list/ ? _list ($self, $arg, $val, $neg) :
214             /keypair/ ? _keypair ($self, $arg, $val, $neg) :
215             throw_usage("can't handle type '$_'");
216             }
217             } else {
218 16 100       73 throw_argv("Invalid argument: $orig")
219             if $orig =~ /^-./; # invalid if looks like it could be an arg;
220 5         9 push @passthrough, $orig;
221             }
222             }
223 103         230 _recalculate_options($self);
224 103         126 @{$self->{target}} = (@passthrough, @{$self->{target}});
  103         137  
  103         135  
225 103         229 return $self;
226             }
227              
228 12     12   57043 BEGIN { *getopts = \&getopt }; # handy alias
229              
230             #--------------------------------------------------------------------------#
231             # validate
232             #--------------------------------------------------------------------------#
233              
234             sub validate {
235 83     83 1 111 my ($self, $arg) = @_;
236 83 50 33     283 throw_usage("Getopt::Lucid->validate() takes a hashref argument")
237             if $arg && ref($arg) ne 'HASH';
238              
239 83 100 66     196 if ( $arg && exists $arg->{requires} ) {
240 7         11 my $requires = $arg->{requires};
241 7 50 33     26 throw_usage("'validate' argument 'requires' must be an array reference")
242             if $requires && ref($requires) ne 'ARRAY';
243 7         12 for my $p ( @$requires ) {
244             throw_spec("Requiring an unspecified option ('$p') in validate()")
245 7 50       16 unless exists $self->{spec}{$p};
246             throw_argv( "Required option '$self->{spec}{$p}{canon}' not found")
247 7 100       23 if ( !$self->{seen}{$p} );
248             }
249             }
250              
251 81         130 _check_prereqs($self);
252              
253 76         142 return $self;
254             }
255              
256             #--------------------------------------------------------------------------#
257             # merge_defaults()
258             #--------------------------------------------------------------------------#
259              
260             sub merge_defaults {
261 8     8 1 2740 my $self = shift;
262             my %merge =
263 8 100       33 ref $_[0] eq 'HASH' ? %{+shift} :
  4 100       13  
264             (@_ % 2 == 0) ? @_ :
265             throw_usage("Argument to merge_defaults() must be a hash or hash reference");
266 7         11 for my $name ( keys %{$self->{spec}} ) {
  7         22  
267 47         64 my $spec = $self->{spec}{$name};
268 47         64 my $strip = $self->{strip}{$name};
269 47 100       77 next unless exists $merge{$strip};
270 29         40 for ( $self->{spec}{$name}{type} ) {
271 29 100       95 /switch|counter|parameter/ && do {
272 19         33 $self->{default}{$strip} = $merge{$strip};
273 19         43 last;
274             };
275 10 100       24 /list/ && do {
276             throw_usage("Option '$strip' in merge_defaults() must be scalar or array reference")
277 5 100 100     24 if ref($merge{$strip}) && ref($merge{$strip}) ne 'ARRAY';
278             $merge{$strip} = ref($merge{$strip}) eq 'ARRAY'
279             ? dclone( $merge{$strip} )
280 4 100       38 : [ $merge{$strip} ] ;
281 4         8 $self->{default}{$strip} = $merge{$strip};
282 4         5 last;
283             };
284 5 50       16 /keypair/ && do {
285             throw_usage("Option '$strip' in merge_defaults() must be scalar or hash reference")
286 5 100 66     32 if ref($merge{$strip}) && ref($merge{$strip}) ne 'HASH';
287 4         42 $self->{default}{$strip} = dclone($merge{$strip});
288 4         17 last;
289             };
290             }
291             throw_spec("Default '$spec->{canon}' = '$self->{default}{$strip}' fails to validate")
292 27 100       62 unless _validate_value($self, $self->{default}{$strip}, $spec->{valid} );
293             }
294 4         13 _recalculate_options($self);
295 4         11 return $self->options;
296             }
297              
298             #--------------------------------------------------------------------------#
299             # names()
300             #--------------------------------------------------------------------------#
301              
302             sub names {
303 1     1 1 3 my ($self) = @_;
304 1         1 return values %{$self->{strip}};
  1         4  
305             }
306              
307              
308             #--------------------------------------------------------------------------#
309             # options()
310             #--------------------------------------------------------------------------#
311              
312             sub options {
313 122     122 1 244 my ($self) = @_;
314 122         129 return %{dclone($self->{options})};
  122         2941  
315             }
316              
317             #--------------------------------------------------------------------------#
318             # replace_defaults()
319             #--------------------------------------------------------------------------#
320              
321             sub replace_defaults {
322 8     8 1 4889 my $self = shift;
323             my %replace =
324 8 100       33 ref $_[0] eq 'HASH' ? %{+shift} :
  4 100       14  
325             (@_ % 2 == 0) ? @_ :
326             throw_usage("Argument to replace_defaults() must be a hash or hash reference");
327 7         13 for my $name ( keys %{$self->{spec}} ) {
  7         21  
328 46         68 my $spec = $self->{spec}{$name};
329 46         61 my $strip = $self->{strip}{$name};
330 46         70 for ( $self->{spec}{$name}{type} ) {
331 46 100       131 /switch|counter/ && do {
332 14   100     43 $self->{default}{$strip} = $replace{$strip} || 0;
333 14         19 last;
334             };
335 32 100       63 /parameter/ && do {
336 19         34 $self->{default}{$strip} = $replace{$strip};
337 19         30 last;
338             };
339 13 100       26 /list/ && do {
340             throw_usage("Option '$strip' in replace_defaults() must be scalar or array reference")
341 7 100 100     29 if ref($replace{$strip}) && ref($replace{$strip}) ne 'ARRAY';
342 6 100       12 if ( exists $replace{$strip} ) {
343             $replace{$strip} = ref($replace{$strip}) eq 'ARRAY' ?
344 4 100       10 $replace{$strip} : [ $replace{$strip} ];
345             } else {
346 2         5 $replace{$strip} = [];
347             }
348 6         111 $self->{default}{$strip} = dclone($replace{$strip});
349 6         10 last;
350             };
351 6 50       15 /keypair/ && do {
352             throw_usage("Option '$strip' in replace_defaults() must be scalar or hash reference")
353 6 100 100     31 if ref($replace{$strip}) && ref($replace{$strip}) ne 'HASH';
354 5 100       9 $replace{$strip} = {} unless exists $replace{$strip};
355 5         48 $self->{default}{$strip} = dclone($replace{$strip});
356 5         38 last;
357             };
358             }
359             throw_spec("Default '$spec->{canon}' = '$self->{default}{$strip}' fails to validate")
360 44 100       117 unless _validate_value($self, $self->{default}{$strip}, $spec->{valid} );
361             }
362 4         13 _recalculate_options($self);
363 4         11 return $self->options;
364             }
365              
366             #--------------------------------------------------------------------------#
367             # reset_defaults()
368             #--------------------------------------------------------------------------#
369              
370             sub reset_defaults {
371 6     6 1 12 my ($self) = @_;
372 6         12 _set_defaults($self);
373 6         12 _recalculate_options($self);
374 6         12 return $self->options;
375             }
376              
377             #--------------------------------------------------------------------------#
378             # usage()
379             #--------------------------------------------------------------------------#
380              
381             sub usage {
382 1     1 1 2 my ($self) = @_;
383 1         2 my @short_opts;
384             my @doc;
385 1         1 for my $opt ( sort { $a->{strip} cmp $b->{strip} } values %{$self->{spec}} ) {
  14         16  
  1         6  
386 7         9 my $names = [ @{ $opt->{names} } ];
  7         13  
387             push @doc, [
388             _build_usage_left_column( $names, \@short_opts ),
389 7         12 _build_usage_right_column( $opt->{doc}, $opt->{default}, $opt->{type} ),
390             ];
391             }
392              
393 1         2 my $max_width = 3 + List::Util::max( map { length } @doc );
  7         13  
394              
395 1         22 my $prog = File::Basename::basename($0);
396              
397 1         3 local $" = '';
398             my $usage = "Usage: $prog [-@short_opts] [long options] [arguments]\n"
399 1         3 . join( "", map { sprintf( "\t%-${max_width}s %s\n", @$_ ) } @doc );
  7         22  
400             }
401              
402             sub _build_usage_left_column {
403 7     7   8 my ($names, $all_short_opts) = @_;
404             my @sorted_names =
405 7         8 sort { length $a <=> length $b } map { my $s = $_; $s =~ s/^-*//; $s } @$names;
  1         2  
  8         9  
  8         17  
  8         19  
406              
407 7         12 my @short_opts = grep { length == 1 } @sorted_names;
  8         12  
408 7         9 my @long_opts = grep { length > 1 } @sorted_names;
  8         12  
409              
410 7         10 push @$all_short_opts, @short_opts;
411              
412             my $group = sub {
413 8     8   9 my $list = shift;
414 8 50       45 '-' . ( @$list == 1 ? $list->[0] : '[' . join( '|', @$list ) . ']' );
415 7         18 };
416             my $prepare = sub {
417 14     14   12 my $list = shift;
418 14 100       30 return ( length $list->[0] > 1 ? '-' : '' ) . $group->($list) if @$list;
    100          
419 6         11 return;
420 7         11 };
421              
422 7         11 return join ', ' => map { $prepare->($_) } \@short_opts, \@long_opts;
  14         18  
423             }
424              
425             sub _build_usage_right_column {
426 7     7   16 my ( $doc, $default, $type ) = @_;
427 7 100       13 my $str = defined $doc ? $doc : '';
428 7 100       15 return $str unless defined $default;
429 4 100       7 $str .= " " if length $str;
430 4         5 $str .= "(default: ";
431 4 100       8 if ($type eq 'list') {
    100          
432 1         3 $str .= join( ", ", @$default );
433             }
434             elsif ( $type eq 'keypair' ) {
435 1         4 $str .= join( ", ", map { "$_=$default->{$_}" } sort keys %$default );
  2         6  
436             }
437             else {
438 2         3 $str .= $default
439             }
440 4         5 $str .= ')';
441 4         10 return $str;
442             }
443              
444             #--------------------------------------------------------------------------#
445             # _check_prereqs()
446             #--------------------------------------------------------------------------#
447              
448             sub _check_prereqs {
449 81     81   91 my ($self) = @_;
450 81         75 for my $key ( keys %{$self->{seen}} ) {
  81         166  
451 202 100       269 next unless $self->{seen}{$key};
452 104 100       165 next unless exists $self->{spec}{$key}{needs};
453 10         12 for (@{$self->{spec}{$key}{needs}}) {
  10         18  
454             throw_argv("Option '$self->{spec}{$key}{canon}' ".
455             "requires option '$self->{spec}{$_}{canon}'")
456 12 100       39 unless $self->{seen}{$_};
457             }
458             }
459             }
460              
461             #--------------------------------------------------------------------------#
462             # _counter()
463             #--------------------------------------------------------------------------#
464              
465             sub _counter {
466 82     82   138 my ($self, $arg, $val, $neg) = @_;
467 82 100       123 throw_argv("Counter option can't take a value: $self->{spec}{$arg}{canon}=$val")
468             if defined $val;
469 80         79 push @{$self->{parsed}}, [ $arg, 1, $neg ];
  80         215  
470             }
471              
472             #--------------------------------------------------------------------------#
473             # _find_arg()
474             #--------------------------------------------------------------------------#
475              
476             sub _find_arg {
477 344     344   459 my ($self, $arg) = @_;
478              
479 344 100       910 $arg =~ s/^-*// unless $self->{strict};
480 344 100       837 return $self->{alias_hr}{$arg} if exists $self->{alias_hr}{$arg};
481              
482 66         72 for ( keys %{$self->{alias_nocase}} ) {
  66         155  
483 14 100       89 return $self->{alias_nocase}{$_} if $arg =~ /^$_$/i;
484             }
485              
486 62         142 return;
487             }
488              
489             #--------------------------------------------------------------------------#
490             # _keypair()
491             #--------------------------------------------------------------------------#
492              
493             sub _keypair {
494 24     24   44 my ($self, $arg, $val, $neg) = @_;
495 24         32 my ($key, $data);
496 24 100       37 if ($neg) {
497 4         33 $key = $val;
498             }
499             else {
500 20 100       36 my $value = defined $val ? $val : shift @{$self->{target}};
  17         27  
501 20 100 100     76 if (! defined $val && ! defined $value) {
502 1         5 throw_argv("Option '$self->{spec}{$arg}{canon}' requires a value");
503             }
504              
505 19 100       75 throw_argv("Badly formed keypair for '$self->{spec}{$arg}{canon}'")
506             unless $value =~ /[^=]+=.+/;
507 16         54 ($key, $data) = ( $value =~ /^([^=]*)=(.*)$/ ) ;
508             throw_argv("Invalid keypair '$self->{spec}{$arg}{canon}': $key => $data")
509             unless _validate_value($self, { $key => $data },
510 16 100       58 $self->{spec}{$arg}{valid});
511             }
512 16         30 push @{$self->{parsed}}, [ $arg, [ $key, $data ], $neg ];
  16         86  
513             }
514              
515             #--------------------------------------------------------------------------#
516             # _list()
517             #--------------------------------------------------------------------------#
518              
519             sub _list {
520 20     20   42 my ($self, $arg, $val, $neg) = @_;
521 20         26 my $value;
522 20 100       30 if ($neg) {
523 4         4 $value = $val;
524             }
525             else {
526 16 100       28 $value = defined $val ? $val : shift @{$self->{target}};
  13         25  
527 16 100       37 if (! defined $val) {
528 13 100       25 if (! defined $value) {
529 1         4 throw_argv("Option '$self->{spec}{$arg}{canon}' requires a value");
530             }
531 12         87 $value =~ s/^$NEGATIVE(.*)$/$1/;
532             }
533              
534 15 100 100     46 throw_argv("Ambiguous value for $self->{spec}{$arg}{canon} could be option: $value")
535             if ! defined $val and _find_arg($self, $value);
536             throw_argv("Invalid list option $self->{spec}{$arg}{canon} = $value")
537 14 100       44 unless _validate_value($self, $value, $self->{spec}{$arg}{valid});
538             }
539 17         25 push @{$self->{parsed}}, [ $arg, $value, $neg ];
  17         59  
540             }
541              
542             #--------------------------------------------------------------------------#
543             # _parameter()
544             #--------------------------------------------------------------------------#
545              
546             sub _parameter {
547 52     52   87 my ($self, $arg, $val, $neg) = @_;
548 52         63 my $value;
549 52 100       78 if ($neg) {
550 6 100       13 throw_argv("Negated parameter option can't take a value: $self->{spec}{$arg}{canon}=$val")
551             if defined $val;
552             }
553             else {
554 46 100       66 $value = defined $val ? $val : shift @{$self->{target}};
  39         56  
555 46 100       86 if (! defined $val) {
556 39 100       55 if (! defined $value) {
557 2         9 throw_argv("Option '$self->{spec}{$arg}{canon}' requires a value");
558             }
559 37         144 $value =~ s/^$NEGATIVE(.*)$/$1/;
560             }
561 44 100 100     118 throw_argv("Ambiguous value for $self->{spec}{$arg}{canon} could be option: $value")
562             if ! defined $val and _find_arg($self, $value);
563             throw_argv("Invalid parameter $self->{spec}{$arg}{canon} = $value")
564 41 100       111 unless _validate_value($self, $value, $self->{spec}{$arg}{valid});
565             }
566 42         64 push @{$self->{parsed}}, [ $arg, $value, $neg ];
  42         134  
567             }
568              
569             #--------------------------------------------------------------------------#
570             # _parse_spec()
571             #--------------------------------------------------------------------------#
572              
573             sub _parse_spec {
574 189     189   231 my ($self) = @_;
575 189         219 my $spec = $self->{raw_spec};
576 189         243 for my $v ( @$spec ) {
577 541         602 my $type = ref $v;
578 541 50       854 throw_spec(
579             "'$type' is not a valid option type"
580             ) unless $type eq 'Getopt::Lucid::Spec';
581             }
582 189         286 for my $opt ( map { $_->_clone } @$spec ) {
  541         712  
583 541         665 my $name = $opt->{name};
584 541         1053 my @names = split( /\|/, $name );
585 541         752 $opt->{canon} = $names[0];
586 541         974 _validate_spec($self,\@names,$opt);
587 533         771 $opt->{names} = \@names;
588 533         1181 ($opt->{strip} = $names[0]) =~ s/^-+//;
589 533 100       974 @names = map { s/^-*//; $_ } @names unless $self->{strict}; ## no critic
  800         1454  
  800         1388  
590 533         735 for (@names) {
591 848         1236 $self->{alias_hr}{$_} = $names[0];
592 848 100       1252 $self->{alias_nocase}{$_} = $names[0] if $opt->{nocase};
593             }
594 533         759 $self->{spec}{$names[0]} = $opt;
595 533         842 $self->{strip}{$names[0]} = $opt->{strip};
596             }
597 181         324 _validate_prereqs($self);
598             }
599              
600             #--------------------------------------------------------------------------#
601             # _recalculate_options()
602             #--------------------------------------------------------------------------#
603              
604             sub _recalculate_options {
605 121     121   143 my ($self) = @_;
606 121         149 my %result;
607 121         118 for my $k ( keys %{$self->{default}} ) {
  121         292  
608 463         582 my $d = $self->{default}{$k};
609 463 100       979 $result{$k} = ref($d) eq 'ARRAY' ? [ @$d ] :
    100          
610             ref($d) eq 'HASH' ? { %$d } : $d;
611             }
612 121         156 for my $opt ( @{$self->{parsed}} ) {
  121         178  
613 274         379 my ($name, $value, $neg) = @$opt;
614 274         354 for ($self->{spec}{$name}{type}) {
615 274         316 my $strip = $self->{strip}{$name};
616 274 100       434 /switch/ && do {
617 59 100       103 $result{$strip} = $neg ? 0 : $value;
618 59         80 last;
619             };
620 215 100       298 /counter/ && do {
621 102 100       172 $result{$strip} = $neg ? 0 : $result{$strip} + $value;
622 102         134 last;
623             };
624 113 100       173 /parameter/ && do {
625 42 100       78 $result{$strip} = $neg ? "" : $value;
626 42         61 last;
627             };
628 71 100       114 /list/ && do {
629 40 100       51 if ($neg) {
630             $result{$strip} = $value ?
631 4 100       7 [ grep { $_ ne $value } @{$result{$strip}} ] :
  4         8  
  2         4  
632             [];
633             }
634 36         34 else { push @{$result{$strip}}, $value }
  36         60  
635 40         55 last;
636             };
637 31 50       57 /keypair/ && do {
638 31 100       43 if ($neg) {
639 4 100       6 if ($value->[0]) { delete $result{$strip}{$value->[0]} }
  2         5  
640 2         3 else { $result{$strip} = {} }
641             }
642 27         59 else { $result{$strip}{$value->[0]} = $value->[1]};
643 31         49 last;
644             };
645             }
646             }
647 121         249 return $self->{options} = \%result;
648             }
649              
650             #--------------------------------------------------------------------------#
651             # _regex_or_code
652             #--------------------------------------------------------------------------#
653              
654             sub _regex_or_code {
655 174     174   232 my ($value,$valid) = @_;
656 174 100       224 return 1 unless defined $valid;
657 171 100       282 if ( ref($valid) eq 'CODE' ) {
658 10         11 local $_ = $value;
659 10         20 return $valid->($value);
660             } else {
661 161         2751 return $value =~ /^$valid$/;
662             }
663             }
664              
665             #--------------------------------------------------------------------------#
666             # _set_defaults()
667             #--------------------------------------------------------------------------#
668              
669             sub _set_defaults {
670 186     186   225 my ($self) = @_;
671 186         190 my %default;
672 186         182 for my $k ( keys %{$self->{spec}} ) {
  186         306  
673 588         776 my $spec = $self->{spec}{$k};
674 588 100       819 my $d = exists ($spec->{default}) ? $spec->{default} : undef;
675 588         715 my $type = $self->{spec}{$k}{type};
676 588         629 my $strip = $self->{strip}{$k};
677 588 50 100     1083 throw_spec("Default for list '$spec->{canon}' must be array reference")
      66        
678             if ( $type eq "list" && defined $d && ref($d) ne "ARRAY" );
679 588 100 100     1015 throw_spec("Default for keypair '$spec->{canon}' must be hash reference")
      100        
680             if ( $type eq "keypair" && defined $d && ref($d) ne "HASH" );
681 587 100       766 if (defined $d) {
682             throw_spec("Default '$spec->{canon}' = '$d' fails to validate")
683 221 100       404 unless _validate_value($self, $d, $spec->{valid});
684             }
685 586         652 $default{$strip} = do {
686 586         680 local $_ = $type;
687 586 100       3283 /switch/ ? (defined $d ? $d: 0) :
    100          
    100          
    100          
    50          
    100          
    100          
    100          
    100          
688             /counter/ ? (defined $d ? $d: 0) :
689             /parameter/ ? $d :
690             /list/ ? (defined $d ? dclone($d): []) :
691             /keypair/ ? (defined $d ? dclone($d): {}) :
692             undef;
693             };
694             }
695 184         439 $self->{default} = \%default;
696             }
697              
698             #--------------------------------------------------------------------------#
699             # _split_equals()
700             #--------------------------------------------------------------------------#
701              
702             sub _split_equals {
703 272     272   340 my ($self,$raw) = @_;
704 272         292 my ($arg,$val);
705 272 100       1338 if ( $raw =~ /^($NEGATIVE?$VALID_NAME|$SHORT_BUNDLE)=(.*)/ ) {
706 27         67 $arg = $1;
707 27         46 $val = $2;
708             } else {
709 245         313 $arg = $raw;
710             }
711 272         545 return ($arg, $val);
712             }
713              
714             #--------------------------------------------------------------------------#
715             # _switch()
716             #--------------------------------------------------------------------------#
717              
718             sub _switch {
719 54     54   95 my ($self, $arg, $val, $neg) = @_;
720 54 100       105 throw_argv("Switch can't take a value: $self->{spec}{$arg}{canon}=$val")
721             if defined $val;
722 52 100       95 if (! $neg ) {
723             throw_argv("Switch used twice: $self->{spec}{$arg}{canon}")
724 48 100       88 if $self->{seen}{$arg} > 1;
725             }
726 51         47 push @{$self->{parsed}}, [ $arg, 1, $neg ];
  51         166  
727             }
728              
729             #--------------------------------------------------------------------------#
730             # _unbundle()
731             #--------------------------------------------------------------------------#
732              
733             sub _unbundle {
734 272     272   383 my ($self,$arg, $val) = @_;
735 272 100       793 if ( $arg =~ /^$SHORT_BUNDLE$/ ) {
736 24         64 my @flags = split(//,substr($arg,1));
737 24 100       48 unshift @{$self->{target}}, ("-" . pop(@flags) . "=" . $val)
  4         20  
738             if defined $val;
739 24         41 for ( reverse @flags ) {
740 61         54 unshift @{$self->{target}}, "-$_";
  61         108  
741             }
742 24         58 return 1;
743             }
744 248         459 return 0;
745             }
746              
747             #--------------------------------------------------------------------------#
748             # _validate_prereqs()
749             #--------------------------------------------------------------------------#
750              
751             sub _validate_prereqs {
752 181     181   248 my ($self) = @_;
753 181         181 for my $key ( keys %{$self->{spec}} ) {
  181         407  
754 529 100       890 next unless exists $self->{spec}{$key}{needs};
755 18         24 my $needs = $self->{spec}{$key}{needs};
756 18 50       47 my @prereq = ref($needs) eq 'ARRAY' ? @$needs : ( $needs );
757 18         24 for (@prereq) {
758 24 100       33 throw_spec("Prerequisite '$_' for '$self->{spec}{$key}{canon}' is not recognized")
759             unless _find_arg($self,$_);
760 23         34 $_ = _find_arg($self,$_);
761             }
762 17         32 $self->{spec}{$key}{needs} = \@prereq;
763             }
764             }
765              
766              
767             #--------------------------------------------------------------------------#
768             # _validate_spec()
769             #--------------------------------------------------------------------------#
770              
771             sub _validate_spec {
772 541     541   712 my ($self,$names,$details) = @_;
773 541         644 for my $name ( @$names ) {
774 857         972 my $alt_name = $name;
775 857 100       2840 $alt_name =~ s/^-*// unless $self->{strict};
776 857 100       3658 throw_spec(
777             "'$name' is not a valid option name/alias"
778             ) unless $name =~ /^$VALID_NAME$/;
779             throw_spec(
780             "'$name' is not unique"
781 855 100       1536 ) if exists $self->{alias_hr}{$alt_name};
782 852         807 my $strip;
783 852         1432 ($strip = $name) =~ s/^-+//;
784             throw_spec(
785             "'$strip' conflicts with other options"
786 852 50       904 ) if grep { $strip eq $_ } values %{$self->{strip}};
  1044         1950  
  852         1764  
787             }
788 536         1019 for my $key ( keys %$details ) {
789             throw_spec(
790             "'$key' is not a valid option specification key"
791 1910 100       2029 ) unless grep { $key eq $_ } @valid_keys;
  15280         17848  
792             }
793 535         736 my $type = $details->{type};
794             throw_spec(
795             "'$type' is not a valid option type"
796 535 100       586 ) unless grep { $type eq $_ } @valid_types;
  2675         3473  
797             }
798              
799             #--------------------------------------------------------------------------#
800             # _validate_value()
801             #--------------------------------------------------------------------------#
802              
803             sub _validate_value {
804 390     390   750 my ($self, $value, $valid) = @_;
805 390 100       881 return 1 unless defined $valid;
806 102 100       201 if ( ref($value) eq 'HASH' ) {
    100          
807 30         73 my $valid_key = $valid->[0];
808 30         37 my $valid_val = $valid->[1];
809 30         89 while (my ($k,$v) = each %$value) {
810 44 100       80 _regex_or_code($k, $valid_key) or return 0;
811 42 100       79 _regex_or_code($v, $valid_val) or return 0;
812             }
813 26         70 return 1;
814             } elsif ( ref($value) eq 'ARRAY' ) {
815 20         31 for (@$value) {
816 36 50       99 _regex_or_code($_, $valid) or return 0;
817             }
818 20         42 return 1;
819             } else {
820 52         81 return _regex_or_code($value, $valid);
821             }
822             }
823              
824             #--------------------------------------------------------------------------#
825             # AUTOLOAD()
826             #--------------------------------------------------------------------------#
827              
828             sub AUTOLOAD {
829 205     205   101905 my $self = shift;
830 205         280 my $name = $Getopt::Lucid::AUTOLOAD;
831 205         871 $name =~ s/.*:://; # strip fully-qualified portion
832 205 100       2420 return if $name eq "DESTROY";
833 15         81 my ($action, $maybe_opt) = $name =~ /^(get|set)_(.+)/ ;
834 15 50       38 if ($action) {
835             # look for a match
836 15         28 my $opt;
837             SEARCH:
838 15         23 for my $known_opt ( values %{ $self->{strip} } ) {
  15         68  
839 45 100       100 if ( $maybe_opt eq $known_opt ) {
840 12         48 $opt = $known_opt;
841 12         31 last SEARCH;
842             }
843             # try without dashes
844 33         88 (my $fuzzy_opt = $known_opt) =~ s/-/_/g;
845 33 100       77 if ( $maybe_opt eq $fuzzy_opt ) {
846 3         5 $opt = $known_opt;
847 3         9 last SEARCH;
848             }
849             }
850              
851             # throw if no valid option was found
852 15 50       34 throw_usage("Can't $action unknown option '$maybe_opt'")
853             if ! $opt;
854              
855             # handle the accessor if an option was found
856 15 100       36 if ($action eq "set") {
857             $self->{options}{$opt} =
858             ref($self->{options}{$opt}) eq 'ARRAY' ? [@_] :
859 5 100       28 ref($self->{options}{$opt}) eq 'HASH' ? {@_} : shift;
    100          
860              
861             }
862 15         36 my $ans = $self->{options}{$opt};
863 15 100       161 return ref($ans) eq 'ARRAY' ? @$ans :
    100          
864             ref($ans) eq 'HASH' ? %$ans : $ans;
865             }
866 0           my $super = "SUPER::$name";
867 0           $self->$super(@_);
868             }
869              
870             1; # modules must be true
871              
872             __END__