File Coverage

blib/lib/Getopt/Long/Spec/Builder.pm
Criterion Covered Total %
statement 50 54 92.5
branch 33 46 71.7
condition 12 27 44.4
subroutine 9 9 100.0
pod 2 2 100.0
total 106 138 76.8


line stmt bran cond sub pod time code
1 3     3   48052 use strict;
  3         7  
  3         114  
2 3     3   17 use warnings;
  3         4  
  3         181  
3              
4             package Getopt::Long::Spec::Builder;
5             {
6             $Getopt::Long::Spec::Builder::VERSION = '0.002';
7             }
8              
9             # ABSTRACT: Build a Getopt::Long option spec from a set of attributes
10 3     3   67 use Carp;
  3         6  
  3         235  
11 3     3   2149 use Data::Dumper;
  3         14405  
  3         2751  
12              
13             our %DATA_TYPE_MAP = (
14             integer => 'i',
15             int => 'i',
16             string => 's',
17             str => 's',
18             float => 'f',
19             extint => 'o',
20             ext => 'o',
21             );
22              
23             our %DEST_TYPE_MAP = (
24             'array' => '@',
25             'hash' => '%',
26             'scalar' => '',
27             );
28              
29             sub new {
30 1     1 1 96 my ( $class, %params ) = @_;
31 1         6 my $self = bless {%params}, $class;
32 1         4 return $self;
33             }
34              
35             sub build {
36 15     15 1 107 my ( $self, %params ) = @_;
37              
38 15         38 my $name_spec = $self->_build_name_spec( \%params );
39              
40 15         36 my $spec_type = $self->_spec_type( \%params );
41              
42 15 50 66     45 croak "default only valid when spec type is ':'\n"
43             if $params{default_num} and $spec_type ne ':';
44              
45 15 100       53 my $arg_spec = ( $spec_type =~ /[:=]/ ) ? $self->_build_arg_spec( \%params ) : '';
46              
47 15         27 my $spec = $name_spec . $spec_type . $arg_spec;
48              
49 15         55 return $spec;
50             }
51              
52             sub _build_name_spec {
53 15     15   18 my ( $self, $params ) = @_;
54              
55 15 50 0     39 $params->{aliases} ||= [] unless exists $params->{aliases};
56 15 50       42 croak "option parameter [aliases] must be an array ref\n"
57             unless ref $params->{aliases} eq 'ARRAY';
58              
59 34 100       133 my $name_spec = join( '|',
60 15         30 grep { defined $_ and length $_ }
61             $params->{long},
62             $params->{short},
63 15         23 @{ $params->{aliases} }
64             );
65              
66 15         44 return $name_spec;
67             }
68              
69             sub _spec_type {
70 15     15   18 my ( $self, $params ) = @_;
71              
72             # note: keep in mind - order is important here!
73 15 100 100     74 return ':' if defined $params->{val_required} and $params->{val_required} == 0;
74 11 100       29 return '=' if $params->{val_required};
75 7 100       18 return '!' if $params->{negatable};
76 6 50       14 return '' unless defined $params->{opt_type};
77 6 100       20 return '+' if $params->{opt_type} =~ '^incr';
78 5 50       18 return '' if $params->{opt_type} eq 'flag';
79 0 0 0     0 return ':' if defined $params->{default_num}
      0        
      0        
80             or defined $params->{val_type}
81             or defined $params->{destination}
82             or defined $params->{dest_type};
83              
84 0         0 die "Could not determine option type from spec!\n";
85             }
86              
87             sub _build_arg_spec {
88 8     8   9 my ( $self, $params ) = @_;
89              
90 8 50 100     41 my $val_type = $DATA_TYPE_MAP{ lc( $params->{val_type} || 'str' ) }
91             or croak "invalid value type [$params->{val_type}]\n";
92              
93             # special cases for incremental opts or opts with default numeric value
94 8 100       18 $val_type = $params->{default_num} if $params->{default_num};
95 8 100 66     29 $val_type = '+' if $params->{opt_type} =~ /^incr/ and !$params->{val_type};
96              
97             # empty or missing destination type is allowable, so this accounts for that.
98 8 100       20 my $dest_type = !$params->{dest_type} ? '' : $DEST_TYPE_MAP{ $params->{dest_type} };
99 8 50 0     25 croak "invalid destination type [@{[ $params->{dest_type} || '' ]}]\n"
  0         0  
100             unless defined $dest_type;
101              
102             # ah, the little-understood "repeat" clause
103 8         11 my $repeat = '';
104 8 100 100     45 if ( defined $params->{min_vals} || defined $params->{max_vals} ) {
    50          
105 2 50       7 croak "repeat spec not valid when using default value\n" if $params->{default_num};
106 2         3 $repeat .= '{';
107 2 100       7 $repeat .= $params->{min_vals} if defined $params->{min_vals};
108 2 50       11 $repeat .= "," . ( defined $params->{max_vals} ? $params->{max_vals} : '' )
    50          
109             if exists $params->{max_vals};
110 2         3 $repeat .= '}';
111             }
112             elsif ( defined $params->{num_vals} ) {
113 0         0 $repeat .= "{$params->{num_vals}}";
114             }
115              
116 8         30 return $val_type . $dest_type . $repeat;
117             }
118              
119             1 && q{this is probably crazier than the last thing I wrote}; # truth
120              
121              
122             =pod
123              
124             =head1 NAME
125              
126             Getopt::Long::Spec::Builder - Build a Getopt::Long option spec from a set of attributes
127              
128             =head1 VERSION
129              
130             version 0.002
131              
132             =head1 SYNOPSIS
133              
134             This module builds a Getopt::Long option specification from a hash of option
135             parameters as would be returned by Getopt::Long::Spec::Parser->parse($spec)
136             and/or Getopt::Nearly::Everything->opt($opt_name)->attrs().
137              
138             Here's an example of use:
139              
140             use Getopt::Long::Spec::Builder;
141              
142             my %opt_attrs = (
143             opt_type => 'simple'
144             value_required => 1,
145             value_type => 'string',
146             max_vals => '5',
147             dest_type => 'array',
148             min_vals => '1',
149             short => [ 'f' ],
150             long => 'foo',
151             );
152              
153             my $builder = Getopt::Long::Spec::Builder->new();
154             my $spec = $builder->build( %opt_attrs );
155             print $spec; # output: 'foo|f=s@{1,5}'
156              
157             # OR...
158              
159             my $spec =
160             Getopt::Long::Spec::Builder->build( %opt_attrs );
161              
162             =head1 METHODS
163              
164             =head2 new
165              
166             Create a new builder object.
167              
168             =head2 build
169              
170             Build a Getopt::Long option specification from the attributes passed in
171             and return the spec as a string
172              
173             =head1 SEE ALSO
174              
175             =over 4
176              
177             =item * Getopt::Long - info on option specifications
178              
179             =item * Getopt::Long::Spec - parse and build GoL specifications
180              
181             =item * Getopt::Long::Spec::Parser - parse GoL specifications
182              
183             =item * Getopt::Nearly::Everything - the module for which this module was created
184              
185             =back
186              
187             =head1 AUTHOR
188              
189             Stephen R. Scaffidi
190              
191             =head1 COPYRIGHT AND LICENSE
192              
193             This software is copyright (c) 2012 by Stephen R. Scaffidi.
194              
195             This is free software; you can redistribute it and/or modify it under
196             the same terms as the Perl 5 programming language system itself.
197              
198             =cut
199              
200              
201             __END__