File Coverage

lib/Getopt/Valid.pm
Criterion Covered Total %
statement 154 166 92.7
branch 79 104 75.9
condition 32 60 53.3
subroutine 15 16 93.7
pod 8 8 100.0
total 288 354 81.3


line stmt bran cond sub pod time code
1             package Getopt::Valid;
2              
3             =head1 NAME
4              
5             Getopt::Valid - Extended processing and validation of command line options
6              
7             =head1 DESCRIPTION
8              
9             Implements an extended getopt mechanism relying on L but provides extended validation and filtering capabilities.
10              
11             Useful for shell scripts.
12              
13             I wrote this, because i need input validation / processing in most of my scripts. This keeps it formal and readable while
14             not making me re-implement the wheel over and over again.
15              
16             The dependency footprint is rather small (only L).
17              
18             =head1 SYNOPSIS
19              
20             #!/usr/bin/perl
21            
22             use strict;
23             use warnings;
24             use Getopt::Valid;
25              
26             #
27             # VALIDATION DEFINITION
28             #
29              
30             my $validation_ref = {
31            
32             # name of the program
33             name => 'MyScript', # fallback to $0, if not set
34            
35             # version info
36             version => '1.0.1', # fallback to $main::VERSION or "unknown", if not set
37            
38             # the struct of the params
39             struct => [
40            
41             # extended example
42             'somestring|s=s' => {
43             description => 'The description of somestring',
44             constraint => sub { my ( $val ) = @_; return index( $val, '123' ) > -1 },
45             required => 1,
46             },
47            
48             # Example using only validator and fallback to default description.
49             # This value is optional (mind: no "!")
50             'otherstring|o=s' => qr/^([a-z]+)$/, # all lowercase words
51            
52             # Example of using integer key with customized description.
53             # This value is optional (mind the "!")
54             'someint|i=i!' => 'The description of someint',
55            
56             # Bool value using the default description
57             'somebool|b' => undefm
58            
59             # the following is implicit, prints out the usag and exists.. can be overwritten
60             #'help|h' => 'Show this help'
61             ]
62             };
63              
64             #
65             # FUNCTIONAL USAGE
66             #
67              
68             my $validated_args_ref = GetOptionsValid( $validation_ref )
69             || die "Failed to validate input\n".
70             join( "\nERRORS:\n", @Getopt::Valid::ERRORS, "\n\nUSAGE:\n", $Getopt::Valid::USAGE );
71            
72             # acces the user input
73             print "Got $validated_args_ref->{ somestring }\n";
74            
75            
76             #
77             # OBJECT USAGE
78             #
79              
80             my $opt = Getopt::Valid->new( $validation_ref );
81              
82             # collect data from @ARGV.. you could manipulate @ARGV and run again..
83             $opt->collect_argv;
84              
85             # whether validates
86             if ( $opt->validate ) {
87            
88             # acces valid input data
89             print "Got ". $opt->valid_args->{ somestring }. "\n";
90             }
91              
92             # oops, not valid
93             else {
94            
95             # print errors
96             print "Oops ". $opt->errors( " ** " ). "\n";
97            
98             # print usage
99             print $opt->usage();
100             }
101              
102             =head1 VALIDATOR SYNTAX
103              
104             =over
105              
106             =item * name STRING
107              
108             Name of the program. Use in help output.
109              
110             Defaults to $0
111              
112             =item * version VERSION STRING
113              
114             Version of the program. Used in help output. Tries $main::VERSION, if not set.
115              
116             Defaults to "unknown"
117              
118             =item * underscore BOOL
119              
120             Whether "-" characters in arguments shall be rewritten to "_" in the result.
121              
122             Default: 0 (disabled)
123              
124             =item * struct ARRAYREF
125              
126             Structure of the arguments. The order will be respected in the help generation.
127              
128             Can be written in 4 styles
129              
130             =over
131              
132             =item * SCALAR
133              
134             $struct_ref = [
135             'argument-name|a=s' => 'Description text for help',
136             'other-arg' => 'Description text for help'
137             ];
138              
139             =item * REGEXP
140              
141             $struct_ref = [
142             'argument-name|a=s' => qr/(bla|blub),
143             ];
144              
145             =item * CODE
146              
147             $struct_ref = [
148             'argument-name|a=s' => sub {
149             my ( $value, $name, $validator ) = @_;
150             warn "Checking '$name' = '$value'\n";
151             return $value eq 'ok';
152             }
153             ];
154              
155             =item * HASH
156              
157             $struct_ref = [
158             'argument-name|a=s' => {
159            
160             # the description for the help
161             description => 'Description text for help',
162            
163             # default value, if not given
164             default => 'Some Default',
165            
166             # whether required (redundant for bool), default: 0
167             required => 1|0,
168            
169             # constraint can be regexp or code-ref, see above
170             constraint => sub { .. },
171            
172             # modify value before handing to constraint (if any)
173             prefilter => sub {
174             my ( $value, $name, $validator ) = @_;
175             return "THE NEW $value\n";
176             },
177            
178             # modify value after constraint check (if any). runs only if valid (or no constraint).
179             postfilter => sub {
180             my ( $value, $name, $validator ) = @_;
181             return "THE NEW $value\n";
182             },
183            
184             # trigger is called in any case, even if arg not given
185             anytrigger => sub {
186             my ( $value, $name, $validator ) = @_;
187             return ; # ignored
188             },
189            
190             # trigger is called if value is defined / given (independent if validated)
191             settrigger => sub {
192             my ( $value, $name, $validator ) = @_;
193             return ; # ignored
194             },
195            
196             # trigger is called if value is validated (or no validator is given)
197             oktrigger => sub {
198             my ( $value, $name, $validator ) = @_;
199             return ; # ignored
200             },
201            
202             # trigger is called if value is invalid (or no validator is given)
203             oktrigger => sub {
204             my ( $value, $name, $validator ) = @_;
205             return ; # ignored
206             },
207             }
208             ];
209              
210             =back
211              
212             =back
213              
214             =cut
215              
216 5     5   4487 use strict;
  5         10  
  5         196  
217 5     5   27 use warnings;
  5         8  
  5         194  
218              
219 5     5   4793 use version 0.74; our $VERSION = qv( "v0.1.4" );
  5         11544  
  5         35  
220              
221 5     5   6297 use Getopt::Long;
  5         71720  
  5         39  
222              
223 5     5   919 use base qw/ Exporter /;
  5         12  
  5         12736  
224             our @EXPORT = qw/ GetOptionsValid /;
225              
226             our $REQUIRED_STR = '[REQ]';
227             our @ERRORS;
228             our $ERROR;
229             our $USAGE = '';
230              
231             =head1 EXPORTED METHODS
232              
233             In functional context, you can access the errors via C<@Getopt::Valid::ERRORS> and the usage via C<$Getopt::Valid::USAGE>
234              
235             =head2 GetOptionsValid $validator_ref
236              
237             See L
238              
239             =cut
240              
241             sub GetOptionsValid {
242 2     2 1 4 my ( $args_ref ) = @_;
243 2         17 my $self = Getopt::Valid->new( $args_ref );
244 2         11 $self->collect_argv;
245 2         7 $USAGE = $self->usage();
246 2 100       8 if ( $self->validate() ) {
247 1         5 return $self->valid_args;
248             }
249 1         22 return 0;
250             }
251              
252             =head1 CLASS METHODS
253              
254             =head2 new $validator_ref
255              
256             Constructor. See L
257              
258             =cut
259              
260             sub new {
261 17     17 1 10458 my ( $class, $args_ref ) = @_;
262 17 50       54 $class = ref $class if ref $class;
263             die "Usage: Getopt::Valid->new( { name => .., version => .., struct => [ .. ] } )"
264 17 50 33     224 unless $args_ref && ref( $args_ref ) eq 'HASH' && $args_ref->{ struct } && ref( $args_ref->{ struct } ) eq 'ARRAY';
      33        
      33        
265 17         164 ( bless {
266             %$args_ref,
267             collected_args => {},
268             updated_struct => {},
269             simple_struct => {},
270             order_struct => []
271             }, $class )->_parse_struct;
272             }
273              
274             sub _parse_struct {
275 17     17   28 my ( $self ) = @_;
276            
277 17         21 my ( %seen_short, %simple_struct, %updated_struct, @order_pre, @order_struct );
278 17         22 my @struct = @{ $self->{ struct } };
  17         84  
279 17         55 for ( my $i = 0; $i < @struct; $i += 2 ) {
280 42         112 push @order_pre, $struct[ $i ];
281             }
282            
283 17         54 my %struct = @struct;
284 17         28 foreach my $key( @order_pre ) {
285 42         75 my $kstruct = $struct{ $key };
286 42         104 my $required = $key =~ s/!$//;
287 42         126 my $error = 0;
288            
289 42         187 my ( $name, $short, $mode_op, $mode, $constraint, $prefilter, $postfilter,
290             $description, $anytrigger, $settrigger, $oktrigger, $failtrigger, $default );
291 42 50       1823 if ( $key =~ /
292             \A
293             (.+?) # name
294             (?:\|(.+?))? # short
295             (?:
296             (?:
297             ([=:]) # op
298             ([isfo]|[0-9]+) # type
299             |
300             (\+) # increment type
301             )
302             )?$
303             \z
304             /x ) {
305 42         91 $name = $1;
306 42   100     1014 $short = $2 || '';
307 42   100     813 $mode_op = $3 || '';
308 42   50     349 $mode = $4 || $5 || 'b';
309             }
310             else {
311 0         0 die "Could not use key '$key' for validation! RTFM"
312             }
313            
314 42   100     313 my $rstruct = ref( $kstruct ) || '';
315 42 100 33     384 if ( $rstruct eq 'HASH' ) {
    50 66        
    50          
    100          
316             $required = $kstruct->{ required } || 0
317 24 100 50     225 if defined $kstruct->{ required };
318             $constraint = $kstruct->{ constraint }
319 24 100       56 if defined $kstruct->{ constraint };
320             $prefilter = $kstruct->{ prefilter }
321 24 50       54 if defined $kstruct->{ prefilter };
322             $postfilter = $kstruct->{ postfilter }
323 24 100       57 if defined $kstruct->{ postfilter };
324             $anytrigger = $kstruct->{ anytrigger }
325 24 100       51 if defined $kstruct->{ anytrigger };
326             $settrigger = $kstruct->{ settrigger }
327 24 100       45 if defined $kstruct->{ settrigger };
328             $oktrigger = $kstruct->{ oktrigger }
329 24 100       165 if defined $kstruct->{ oktrigger };
330             $failtrigger = $kstruct->{ failtrigger }
331 24 100       70 if defined $kstruct->{ failtrigger };
332             $description = $kstruct->{ description }
333 24 100       368 if defined $kstruct->{ description };
334             $default = $kstruct->{ default }
335 24 50       284 if defined $kstruct->{ default };
336             }
337             elsif ( $rstruct eq 'Regexp' || $rstruct eq 'CODE' ) {
338 0         0 $constraint = $kstruct;
339             }
340             elsif ( $rstruct ) {
341 0         0 die "Invalid constraint for key $name. Neither regexp-ref nor code-ref nor scalar"
342             }
343             elsif ( defined $kstruct && length( $kstruct ) > 1 ) {
344 4         181 $description = $kstruct;
345             }
346            
347 42 100       130 $default = $mode eq 's' ? '' : 0
    50          
348             unless defined $default;
349            
350 42 100       277 $description = "$name value"
351             unless $description;
352            
353 42         770 $updated_struct{ $name } = {
354             required => $required,
355             short => $short,
356             mode => $mode,
357             mode_op => $mode_op,
358             constraint => $constraint,
359             postfilter => $postfilter,
360             prefilter => $prefilter,
361             description => $description,
362             anytrigger => $anytrigger,
363             settrigger => $settrigger,
364             oktrigger => $oktrigger,
365             failtrigger => $failtrigger,
366             default => $default
367             };
368 42         146 $simple_struct{ $key } = $name;
369 42         228 push @order_struct, $name;
370 42         337 $seen_short{ $short } ++;
371             }
372            
373 17 50       55 unless ( defined $updated_struct{ help } ) {
374 17 50       48 my ( $short, $key ) = $seen_short{ h } ? ( undef, 'help' ) : ( 'h', 'help|h' );
375             $updated_struct{ help } = {
376             required => 0,
377             short => $short,
378             mode => 'b',
379             mode_op => '',
380             constraint => undef,
381             postfilter => undef,
382             prefilter => undef,
383             description => 'Show this help',
384             anytrigger => undef,
385 0     0   0 settrigger => sub { print $_[-1]->usage; exit; },
  0         0  
386 17         432 oktrigger => undef,
387             failtrigger => undef,
388             default => 0,
389             };
390 17         210 $simple_struct{ $key } = 'help';
391 17         32 push @order_struct, 'help';
392             }
393            
394 17         2238 $self->{ updated_struct } = \%updated_struct;
395 17         31 $self->{ simple_struct } = \%simple_struct;
396 17         47 $self->{ order_struct } = \@order_struct;
397            
398 17         122 $self;
399             }
400              
401             =head2 collect_argv
402              
403             Collect args found in C<@ARGV> using L
404              
405             =cut
406              
407             sub collect_argv {
408 17     17 1 93 my ( $self ) = @_;
409 17         22 my ( %struct, %args );
410 17   50     20 while( my( $key, $name ) = each %{ $self->{ simple_struct } ||= {} } ) {
  75         484  
411 58         84 $key =~ s/!$//;
412 58         108 my $default = $self->{ updated_struct }->{ $name }->{ default };
413 58         164 $struct{ $key } = \( $args{ $name } = $default );
414             }
415            
416             # DIRY HACK
417             # don't want to have the output of Getopt::Long around here
418 17         41 my $stderr = *STDERR;
419 17         1093 open my $null, '>', File::Spec->devnull;
420 17         40 *STDERR = $null;
421 17         83 GetOptions( %struct );
422 17         7621 *STDERR = $stderr;
423 17         164 close $null;
424            
425 17         59 $self->args( \%args );
426             }
427              
428             =head2 args
429              
430             Set/get args.
431              
432             =cut
433              
434             sub args {
435 34     34 1 48 my ( $self, $args_ref ) = @_;
436 34 100       92 $self->{ collected_args } = $args_ref
437             if $args_ref;
438 34   50     168 return $self->{ collected_args } ||= {};
439             }
440              
441             =head2 validate
442              
443             Performs validation of the input. Returns differently in array- or scalar-context.
444              
445             =over
446              
447             =item * Array context
448              
449             Returns ( has_errors, hashref_of_valid_input )
450              
451             my ( $valid, $input_ref ) = $obj->validate();
452             if ( $valid ) {
453             print "All good, got arg 'some_arg': $input_ref->{ some_arg }\n";
454             }
455              
456             =item * Scalar context
457              
458             Returns whether validation was successfull (or any error ocurred)
459              
460             if ( scalar $obj->validate() ) {
461             print "All good, got arg 'some_arg': ". $opt->valid_args->{ some_arg }. "\n";
462             }
463              
464             =back
465              
466             =cut
467              
468             sub validate {
469 17     17 1 50 my ( $self ) = @_;
470 17         32 my $args_ref = $self->args;
471 17         21 my ( @errors, %valid_args );
472 17   50     21 while( my( $name, $ref ) = each %{ $self->{ updated_struct } ||= {} } ) {
  75         368  
473 58         66 my $error = 0;
474 290         536 my ( $required, $constraint, $description, $prefilter, $postfilter )
475 58         75 = map { $ref->{ $_ } } qw/ required constraint description prefilter postfilter /;
476            
477             # get value
478 58 50       158 my $value = defined $args_ref->{ $name }
479             ? $args_ref->{ $name }
480             : undef;
481            
482             # run pre filter
483 58 50       115 $value = $prefilter->( $value, $name, $self )
484             if $prefilter;
485            
486             # check: required
487 58 100 100     181 if ( $required && ! $value ) {
488 4         14 push @errors, sprintf( 'Required key "%s" not given', $name );
489 4         6 $error++;
490             }
491            
492             # check: constraint
493 58 50 66     232 if ( $constraint && defined $value && ( ref( $value ) || length( $value ) ) ) {
      33        
      66        
494 14 50       35 if ( ref( $constraint ) eq 'CODE' ) {
495 0 0       0 unless ( $constraint->( $value, $name, $self ) ) {
496 0         0 push @errors, sprintf( 'Value of key "%s" is invalid', $name );
497 0         0 $error++;
498             }
499             }
500             else {
501 14 100       84 unless ( $value =~ $constraint ) {
502 6         33 push @errors, sprintf( 'Value of key "%s" is invalid', $name );
503 6         11 $error++;
504             }
505             }
506             }
507            
508             # get triggers
509 232         443 my ( $anytrigger, $settrigger, $oktrigger, $failtrigger )
510 58         77 = map { $ref->{ $_ } } qw/ anytrigger settrigger oktrigger failtrigger /;
511            
512             # no error? valid value -> assi
513 58 100       157 unless ( $error ) {
    100          
514 48 100       171 $value = $postfilter->( $value )
515             if $postfilter;
516 48         224 $valid_args{ $name } = $value;
517            
518             # call ok trigger?
519 48 100       97 $oktrigger->( $value, $name, $self )
520             if $oktrigger;
521             }
522            
523             # call fail trigger?
524             elsif ( $failtrigger ) {
525 1         3 $failtrigger->( $value, $name, $self );
526             }
527            
528             # call any trigger?
529 58 100       111 $anytrigger->( $value, $name, $self )
530             if $anytrigger;
531            
532             # call set trigger?
533 58 100 100     246 $settrigger->( $value, $name, $self )
534             if $settrigger && $value;
535             }
536            
537            
538 17 100       52 if ( $self->{ underscore } ) {
539 12         31 foreach my $k( keys %valid_args ) {
540 30         72 ( my $ku = $k ) =~ s#\-#_#gms;
541 30 100       103 $valid_args{ $ku } = delete $valid_args{ $k }
542             if $ku ne $k;
543             }
544             }
545            
546 17         43 $self->{ valid_args } = \%valid_args;
547 17         34 $self->{ errors } = \@errors;
548 17         39 @ERRORS = @errors;
549 17         39 $ERROR = join( ' ** ', @ERRORS );
550            
551 17 0       88 return wantarray ? ( @errors ? 0 : 1, \%valid_args ) : @errors ? 0 : 1;
    100          
    50          
552             }
553              
554             =head2 valid_args
555              
556             Returns validated args
557              
558             =cut
559              
560             sub valid_args {
561 15     15 1 378 shift->{ valid_args };
562             }
563              
564             =head2 usage
565              
566             Returns usage as string
567              
568             =cut
569              
570             sub usage {
571 3     3 1 10 my ( $self, $do_print ) = @_;
572             my @output = (
573             sprintf( 'Program: %s', $self->{ name } || $0 ),
574 3   33     54 sprintf( 'Version: %s', $self->{ version } || eval { $main::VERSION } || 'unknown' ),
      50        
575             '',
576             'Usage: '. $0. ' ',
577             '',
578             'Parameter:'
579             );
580             my $mode_out = sub {
581 16     16   20 my $m = shift;
582 16 100       65 if ( $m eq 's' ) {
    100          
    50          
    50          
    50          
583 7         17 return 'string';
584             }
585             elsif ( $m eq 'i' ) {
586 3         9 return 'integer';
587             }
588             elsif ( $m eq 'f' ) {
589 0         0 return 'real';
590             }
591             elsif ( $m eq 'o' ) {
592 0         0 return 'octet or hex';
593             }
594             elsif ( $m =~ /^[0-9]|\+$/ ) {
595 0         0 return 'optional:integer'
596             }
597             else {
598 6         14 return 'bool';
599             }
600 3         14 };
601 3         6 foreach my $name( @{ $self->{ order_struct } } ) {
  3         9  
602 16         30 my $ref = $self->{ updated_struct }->{ $name };
603 16         28 my @arg_out = ( ' --'. $name );
604 16 50       53 push @arg_out, ' | -'. $ref->{ short } if $ref->{ short };
605 16         39 push @arg_out, ' : '. $mode_out->( $ref->{ mode } );
606 16         28 my $arg_out = join( '', @arg_out );
607 16 100       56 $arg_out .= ' '. $REQUIRED_STR if $ref->{ required };
608 16         37 push @output, $arg_out;
609            
610             my @description = ref( $ref->{ description } )
611 3         7 ? ( map { ' '. $_ } @{ $ref->{ description } } )
  1         3  
612 16 100       50 : ( ' '. $ref->{ description } );
613 16         29 push @output, join( "\n", @description );
614 16         42 push @output, '';
615             }
616 3         6 push @output, '';
617            
618 3         15 my $output = join( "\n", @output );
619 3 50       9 print $output if $do_print;
620 3         27 return $output;
621             }
622              
623             =head2 errors
624              
625             Returns errors as joined string or array of strings of the last valid-run
626              
627             =cut
628              
629             sub errors {
630 1     1 1 4 my ( $self, $sep ) = @_;
631             return ! $sep && wantarray
632 1   50     21 ? @{ $self->{ errors } ||= [] }
633 1 50 33     14 : join( $sep || "\n", @{ $self->{ errors } ||= [] } );
  0   0        
      0        
634             }
635              
636             =head1 SEE ALSO
637              
638             =over
639              
640             =item * L
641              
642             =item * Latest release on Github L
643              
644             =back
645              
646             =head1 AUTHOR
647              
648             =over
649              
650             =item * Ulrich Kautz
651              
652              
653             =back
654              
655             =head1 COPYRIGHT AND WARRANTY
656              
657             Copyright (c) 2012 the L as listed above.
658              
659             This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
660              
661             =head1 LICENCSE
662              
663             This library is free software and may be distributed under the same terms as perl itself.
664              
665             =cut
666              
667             1;