File Coverage

blib/lib/Filter/Arguments.pm
Criterion Covered Total %
statement 150 153 98.0
branch 69 76 90.7
condition 31 36 86.1
subroutine 9 9 100.0
pod 1 5 20.0
total 260 279 93.1


line stmt bran cond sub pod time code
1             package Filter::Arguments;
2              
3             our $VERSION = '0.14';
4              
5 6     6   152644 use 5.0071;
  6         22  
  6         260  
6 6     6   32 use strict;
  6         8  
  6         247  
7 6     6   33 use warnings FATAL => 'all';
  6         15  
  6         512  
8 6     6   15557 use Filter::Simple;
  6         214506  
  6         49  
9              
10             my @Identifiers;
11             my %Default_For;
12             my %Alias_For;
13              
14             my $Retro_Arguments_Regex = qr{
15             : \s* Arguments? \s*
16             (?: [\(] .*? [\)] \s* )?
17             ( = \s* [^;]+ )? ;
18             }msx;
19              
20             my $Arguments_Regex = qr{
21             ( [\@\$\%] \S+ \s* = \s* )?
22             Arguments? \s*
23             ( [\(] .*? [\)] \s* )?
24             (?= [,;\)\}\]] )
25             }msx;
26              
27             my $Arguments_Usage_Regex = qr{
28             Arguments?::verify_usage \s* (?: [\(] [\)] )?
29             }msx;
30              
31             sub verify_usage {
32 1     1 0 23 my $argv_ra = shift @_;
33 1         21 my %arguments = @_;
34              
35 1         4 my $usage = "$0\n";
36 1         2 my @errors;
37              
38 1         8 while ( my ($arg,$val) = each %arguments) {
39              
40 9         14 my $alias = $Alias_For{$arg};
41 9         11 my $default = $Default_For{$arg};
42              
43 9   66     24 $val ||= $default;
44              
45 9 100       16 if ( !defined $val ) {
46 4         10 push @errors, "no value supplied for $arg";
47             }
48              
49 9         17 $usage .= " $alias";
50 9 100       18 if ( defined $default ) {
51 5 100       7 if ( length $default ) {
52 4         9 $usage .= "\t(default is '$default')";
53             }
54             else {
55 1         3 $usage .= "\t(default is empty string)";
56             }
57             }
58 9         31 $usage .= "\n";
59             }
60              
61 1         6 my %valid_alias = map { ( $Alias_For{$_} => 1 ) } keys %Alias_For;
  9         28  
62              
63 1         3 ARG:
64 1         3 for my $arg (@{ $argv_ra }) {
65              
66             next ARG
67 2 50       10 if $arg !~ m/\A --? /xms;
68              
69 2 100       7 if ( !defined $valid_alias{$arg} ) {
70              
71 1 50       6 if ( $arg =~ m{\A --? (?: help|usage|[?] ) \z}xms ) {
72 0         0 push @errors, "Usage:";
73             }
74             else {
75 1         4 push @errors, "unexpected argument $arg";
76             }
77             }
78             }
79              
80 1 50       4 if ( @errors ) {
81              
82 1         3 for my $error (@errors) {
83 5         40 warn "$error\n";
84             }
85 1         20 die "$usage\n";
86             }
87 0         0 return 1;
88             }
89              
90             sub parse {
91 64     64 0 3289 my ( $identifier, $params_rh, $argv_ra ) = @_;
92              
93 64         691 $identifier =~ s{ (?: \A [\(]? \s* | \s* [\)]? ) }{}xmsg;
94              
95 64 100       196 if ( $identifier =~ m{,} ) {
96              
97 8         55 my @idents = split /\s*,\s*/, $identifier;
98 8         37 my @results;
99 8         27 for my $i ( 0 .. $#idents ) {
100              
101 24         36 my $ident = $idents[$i];
102 24         32 my %params = %{ $params_rh };
  24         63  
103              
104 24 100 100     108 if ( defined $params{default} && ref $params{default} eq 'ARRAY' ) {
105              
106 6 50       7 if ( $i <= $#{ $params{default} } ) {
  6         22  
107              
108 6         13 $params{default} = $params{default}->[$i];
109             }
110             else {
111              
112 0         0 delete $params{default};
113             }
114             }
115              
116 24         87 push @results, parse( $ident, \%params, $argv_ra );
117             }
118 8         40 return @results;
119             }
120              
121 56         233 my ($sigil,$ident) = $identifier =~ m/\A ( \W+ ) ( \w+ ) \z/xms;
122 56   100     136 $sigil ||= "";
123 56   100     110 $ident ||= "";
124              
125 56 100       250 my $type
    100          
    100          
    100          
126             = $sigil eq '@' ? 'ARRAY'
127             : $sigil eq '%' ? 'HASH'
128             : $ident =~ m/ _ra \z/xmsi ? 'ARRAY'
129             : $ident =~ m/ _rh \z/xmsi ? 'HASH'
130             : 'SCALAR';
131              
132 56         65 my ($default,$alias);
133              
134 56 50       117 if ( $params_rh ) {
135              
136 56 50       135 if ( ref $params_rh eq 'HASH' ) {
137              
138 56 100       132 if ( defined $params_rh->{alias} ) {
139 7         17 $alias = $params_rh->{alias};
140             }
141              
142 56 100       120 if ( defined $params_rh->{default} ) {
143 19         30 $default = $params_rh->{default};
144             }
145              
146 56 100 66     186 if ( !$alias && !$default ) {
147              
148 44         179 ALIAS_DEFAULT:
149 35         43 while ( my ($a,$d) = each %{ $params_rh } ) {
150              
151             # Don't allow certain reserved words in this mode
152             # use: alias => 'default' when option --default is needed
153             # use: alias => 'alias' when option --alias is needed
154             next ALIAS_DEFAULT
155 9 100 66     73 if $a eq 'default' || $a eq 'alias';
156              
157 8         46 ($alias,$default) = ($a,$d);
158             }
159             }
160             }
161             }
162              
163 56   66     184 $alias ||= $ident;
164 56         99 $alias =~ s{ _r[ah] \z}{}xmsi;
165 56         113 $alias = "--$alias";
166 56         140 $alias =~ s{\A -{3,} }{--}xms;
167              
168 56         161 $Alias_For{$ident} = $alias;
169 56         103 $Default_For{$ident} = $default;
170              
171 56 100       119 if ( $type eq 'HASH' ) {
172              
173 4         5 my $key;
174             my %arguments;
175              
176 4         10 ARG:
177 4         7 for my $arg_index ( 0 .. $#{ $argv_ra } ) {
178              
179 32         38 my $arg = $argv_ra->[$arg_index];
180              
181 32 100       61 if ( $arg =~ m/\A --? /xms ) {
182              
183 12         15 $key = $arg;
184 12         25 $key =~ s{\A [-]+ }{}xms;
185              
186 12         25 $arguments{$key} = [];
187 12         22 next ARG;
188             }
189             else {
190              
191 20 100       35 if ( !$key ) {
192 2         25 $key = $alias;
193 2         9 $key =~ s{\A [-]+ }{}xms;
194             }
195              
196 20         18 push @{ $arguments{$key} }, $arg;
  20         46  
197             }
198             }
199              
200 4 100 66     18 if ( !keys %arguments && $default ) {
201 1         2 $key = $alias;
202 1         5 $key =~ s{\A [-]+ }{}xms;
203 1         3 $arguments{$key} = [ $default ];
204             }
205              
206 4         9 for my $option (keys %arguments) {
207              
208 15 100       18 if ( @{ $arguments{$option} } == 0 ) {
  15 100       54  
  10         31  
209 5         8 $arguments{$option} = 1;
210             }
211             elsif ( @{ $arguments{$option} } == 1 ) {
212 4         13 $arguments{$option} = $arguments{$option}->[0];
213             }
214             }
215              
216 4 100       30 return %arguments
217             if wantarray;
218              
219 1         6 return \%arguments;
220             }
221              
222             ARG:
223 52         55 for my $arg_index ( 0 .. $#{ $argv_ra } ) {
  52         128  
224              
225 324         567 my $next_index
226 324 100       298 = $arg_index < $#{ $argv_ra }
227             ? $arg_index + 1
228             : undef;
229              
230 324         376 my $arg = $argv_ra->[$arg_index];
231              
232             next ARG
233 324 100       713 if $arg ne $alias;
234              
235 23 100       45 if ( $type eq 'SCALAR' ) {
    50          
236              
237 18 100       36 my $value
238             = defined $next_index
239             ? $argv_ra->[$next_index]
240             : 1;
241              
242 18 100       52 if ( $value =~ m/\A --? /xms ) {
243 9   100     36 $value = $default || 1;
244             }
245              
246 18         69 return $value;
247             }
248             elsif ( $type eq 'ARRAY' ) {
249              
250 5         5 my @values;
251              
252 5         8 VAL:
253 5         6 for my $next_index ( $arg_index + 1 .. $#{ $argv_ra } ) {
254              
255 14         12 my $value = $argv_ra->[$next_index];
256              
257             last VAL
258 14 100       25 if $value =~ m/\A --? /xms;
259              
260 10         14 push @values, $value;
261             }
262              
263             return @values
264 5 100       23 if wantarray;
265              
266 1         4 return \@values;
267             }
268             }
269              
270 29 100 100     169 return @{ $default }
  2   100     30  
271             if wantarray && $default && ref $default eq 'ARRAY';
272              
273 27         105 return $default;
274             }
275              
276             sub retro_transform {
277 7     7 0 59 my ($default) = @_;
278              
279 7   100     329 $default ||= "";
280              
281 7 100       17 if ( $default ) {
282              
283 3         25 $default =~ s{(?: \A [=\s]* | \s* \z )}{}xmsg;
284              
285 3 100       14 if ( $default =~ m{\A [\(] }xms ) {
286              
287 2         12 $default =~ s{(?: \A [\(] | \W \z )}{}xmsg;
288              
289 2         5 $default = "( default => [ $default ] )";
290             }
291             else {
292              
293 1         4 $default = "( default => '$default' )";
294             }
295             }
296              
297 7         394 return "= Arguments$default;";
298             }
299              
300             sub transform {
301 40     40 0 109 my ($assignment,$parameters) = @_;
302              
303 40   100     115 $assignment ||= "";
304 40   100     285 $parameters ||= "";
305              
306 40         69 my $identifiers = $assignment;
307 40         311 $identifiers =~ s{(?: \A \s* [^\@\$\%\(]* | \W* \z )}{}xmsg;
308 40         493 $identifiers =~ s{\s*}{}xmsg;
309              
310 40         136 push @Identifiers, split /\s*,\s*/, $identifiers;
311              
312 40         351 return "${assignment}Filter::Arguments::parse(q{$identifiers},{$parameters},\\\@ARGV)";
313             }
314              
315             sub usage {
316              
317 1     1 1 2 my $arg_string = "";
318              
319 1         3 for my $identifier (@Identifiers) {
320              
321 9         13 my $key = $identifier;
322 9         11 my $val = $identifier;
323              
324 9         334 $key =~ s{\A \W }{}xmsg;
325              
326 9         33 $arg_string .= " $key => $val,";
327             }
328              
329 1         9 return "Filter::Arguments::verify_usage(\\\@ARGV,$arg_string)";
330             }
331              
332             FILTER_ONLY
333             code_no_comments => sub {
334              
335             # for backward compatibility
336             $_ =~ s{$Retro_Arguments_Regex}{retro_transform($1)}xmsge;
337              
338             $_ =~ s{$Arguments_Regex}{transform($1,$2)}xmsge;
339              
340             $_ =~ s{$Arguments_Usage_Regex}{usage()}xmsge;
341             };
342              
343             1;
344              
345             __END__