File Coverage

blib/lib/Perinci/Sub/GetArgs/Argv.pm
Criterion Covered Total %
statement 449 474 94.7
branch 192 234 82.0
condition 95 128 74.2
subroutine 30 31 96.7
pod 2 2 100.0
total 768 869 88.3


line stmt bran cond sub pod time code
1             package Perinci::Sub::GetArgs::Argv;
2              
3             our $AUTHORITY = 'cpan:PERLANCAR'; # AUTHORITY
4             our $DATE = '2020-09-09'; # DATE
5             our $DIST = 'Perinci-Sub-GetArgs-Argv'; # DIST
6             our $VERSION = '0.845'; # VERSION
7              
8 3     3   242780 use 5.010001;
  3         36  
9 3     3   18 use strict;
  3         6  
  3         62  
10 3     3   15 use warnings;
  3         7  
  3         85  
11             #use Log::Any '$log';
12              
13 3     3   1068 use Data::Sah::Normalize qw(normalize_schema);
  3         3367  
  3         197  
14 3     3   2235 use Data::Sah::Util::Type qw(is_type is_simple);
  3         3433  
  3         234  
15 3     3   1592 use Getopt::Long::Negate::EN qw(negations_for_option);
  3         2706  
  3         184  
16 3     3   1653 use Getopt::Long::Util qw(parse_getopt_long_opt_spec);
  3         8488  
  3         228  
17 3     3   25 use List::Util qw(first);
  3         6  
  3         254  
18 3     3   1569 use Perinci::Sub::GetArgs::Array qw(get_args_from_array);
  3         2322  
  3         194  
19 3     3   1663 use Perinci::Sub::Util qw(err);
  3         9126  
  3         196  
20              
21 3     3   23 use Exporter;
  3         6  
  3         727  
22             our @ISA = qw(Exporter);
23             our @EXPORT_OK = qw(
24             gen_getopt_long_spec_from_meta
25             get_args_from_argv
26             );
27              
28             our %SPEC;
29              
30             $SPEC{':package'} = {
31             v => 1.1,
32             summary => 'Get subroutine arguments from command line arguments (@ARGV)',
33             };
34              
35             # retun ($success?, $errmsg, $res)
36             sub _parse_json {
37 8     8   20 my $str = shift;
38              
39 8         14 state $json = do {
40 1         915 require JSON::PP;
41 1         14967 JSON::PP->new->allow_nonref;
42             };
43              
44             # to rid of those JSON::PP::Boolean objects which currently choke
45             # Data::Sah-generated validator code. in the future Data::Sah can be
46             # modified to handle those, or we use a fork of JSON::PP which doesn't
47             # produce those in the first place (probably only when performance is
48             # critical).
49 8         110 state $cleanser = do {
50 1 50       3 if (eval { require Data::Clean::FromJSON; 1 }) {
  1         521  
  1         5925  
51 1         8 Data::Clean::FromJSON->get_cleanser;
52             } else {
53 0         0 undef;
54             }
55             };
56              
57 8         2052 my $res;
58 8 50       18 eval { $res = $json->decode($str); $cleanser->clean_in_place($res) if $cleanser };
  8         48  
  4         861  
59 8         1998 my $e = $@;
60 8         36 return (!$e, $e, $res);
61             }
62              
63             sub _parse_yaml {
64 3     3   23 no warnings 'once';
  3         7  
  3         15582  
65              
66 5     5   11 state $yaml_xs_available = do {
67 1 50       2 if (eval { require YAML::XS; 1 }) {
  1         6  
  1         5  
68 1         3 1;
69             } else {
70 0         0 require YAML::Old;
71 0         0 0;
72             }
73             };
74              
75 5         14 my $str = shift;
76              
77             #local $YAML::Syck::ImplicitTyping = 1;
78 5         10 my $res;
79 5         8 eval {
80 5 50       17 if ($yaml_xs_available) {
81 5         302 $res = YAML::XS::Load($str);
82             } else {
83             # YAML::Old is too strict, it requires "--- " header and newline
84             # ending
85 0 0       0 $str = "--- $str" unless $str =~ /\A--- /;
86 0 0       0 $str .= "\n" unless $str =~ /\n\z/;
87 0         0 $res = YAML::Old::Load($str);
88             }
89             };
90 5         21 my $e = $@;
91 5         25 return (!$e, $e, $res);
92             }
93              
94             sub _arg2opt {
95 244     244   404 my $opt = shift;
96 244         753 $opt =~ s/[^A-Za-z0-9-]+/-/g; # foo.bar_baz becomes --foo-bar-baz
97 244         510 $opt;
98             }
99              
100             # this subroutine checks whether a schema mentions a coercion rule from simple
101             # types (e.g. 'str_comma_sep', etc).
102             sub _is_coercible_from_simple {
103 192     192   3941 my $nsch = shift;
104 192 50       411 my $cset = $nsch->[1] or return 0;
105 192 100 66     910 my $rules = $cset->{'x.perl.coerce_rules'} // $cset->{'x.coerce_rules'}
106             or return 0;
107 2         5 for my $rule (@$rules) {
108 2 50       12 next unless $rule =~ /\A([^_]+)_/;
109 2 50       5 return 1 if is_simple($1);
110             }
111 0         0 0;
112             }
113              
114             sub _is_simple_or_coercible_from_simple {
115 561     561   887 my $nsch = shift;
116 561 100       1156 is_simple($nsch) || _is_coercible_from_simple($nsch);
117             }
118              
119             # this routine's job is to avoid using Data::Sah::Resolve unless it needs to, to
120             # reduce startup overhead
121             sub _is_simple_or_array_of_simple_or_hash_of_simple {
122 462     462   93832 my $nsch = shift;
123              
124 462         679 my $is_simple = 0;
125 462         614 my $is_array_of_simple = 0;
126 462         645 my $is_hash_of_simple = 0;
127 462         591 my $eltype;
128              
129 462         829 my $type = $nsch->[0];
130 462         637 my $cset = $nsch->[1];
131              
132             {
133             # if not known as builtin type, then resolve it first
134 462 100       662 unless (is_type($nsch)) {
  462         1118  
135 1         567 require Data::Sah::Resolve;
136 1         1154 my $res = Data::Sah::Resolve::resolve_schema(
137             {merge_clause_sets => 0}, $nsch);
138 1         24393 $type = $res->[0];
139 1   50     8 $cset = $res->[1][0] // {};
140             }
141              
142 462         9043 $is_simple = _is_simple_or_coercible_from_simple([$type, $cset]);
143 462 100       6235 last if $is_simple;
144              
145 188 100       410 if ($type eq 'array') {
146 104   66     282 my $elnsch = $cset->{of} // $cset->{each_elem};
147 104 100       221 last unless $elnsch;
148 64         156 $elnsch = normalize_schema($elnsch);
149 64         2976 $eltype = $elnsch->[0];
150              
151             # if not known as builtin type, then resolve it first
152 64 100       149 unless (is_type($elnsch)) {
153 1         25 require Data::Sah::Resolve;
154 1         7 my $res = Data::Sah::Resolve::resolve_schema(
155             {merge_clause_sets => 0}, $elnsch);
156 1   50     302 $elnsch = [$res->[0], $res->[1][0] // {}]; # XXX we only take the first clause set
157 1         5 $eltype = $res->[0];
158             }
159              
160 64         1128 $is_array_of_simple = _is_simple_or_coercible_from_simple($elnsch);
161 64         1273 last;
162             }
163              
164 84 100       221 if ($type eq 'hash') {
165 83   66     300 my $elnsch = $cset->{of} // $cset->{each_value} // $cset->{each_elem};
      33        
166 83 100       178 last unless $elnsch;
167 35         118 $elnsch = normalize_schema($elnsch);
168 35         678 $eltype = $elnsch->[0];
169              
170             # if not known as builtin type, then resolve it first
171 35 100       80 unless (is_type($elnsch)) {
172 1         32 require Data::Sah::Resolve;
173 1         7 my $res = Data::Sah::Resolve::resolve_schema(
174             {merge_clause_sets => 0}, $elnsch);
175 1   50     297 $elnsch = [$res->[0], $res->[1][0] // {}]; # XXX we only take the first clause set
176 1         4 $eltype = $res->[0];
177             }
178              
179 35         641 $is_hash_of_simple = _is_simple_or_coercible_from_simple($elnsch);
180 35         695 last;
181             }
182             }
183              
184             #{ no warnings 'uninitialized'; say "D:$nsch->[0]: is_simple=<$is_simple>, is_array_of_simple=<$is_array_of_simple>, is_hash_of_simple=<$is_hash_of_simple>, type=<$type>, eltype=<$eltype>" };
185 462         1588 ($is_simple, $is_array_of_simple, $is_hash_of_simple, $type, $cset, $eltype);
186             }
187              
188             # return one or more triplets of Getopt::Long option spec, its parsed structure,
189             # and extra stuffs. we do this to avoid having to call
190             # parse_getopt_long_opt_spec().
191             sub _opt2ospec {
192 234     234   494 my ($opt, $schema, $arg_spec) = @_;
193 234         437 my ($is_simple, $is_array_of_simple, $is_hash_of_simple, $type, $cset, $eltype) =
194             _is_simple_or_array_of_simple_or_hash_of_simple($schema);
195              
196 234         499 my (@opts, @types, @isaos, @ishos);
197              
198 234 100 100     816 if ($is_array_of_simple || $is_hash_of_simple) {
199 54         80 my $singular_opt;
200 54 100 100     190 if ($arg_spec && $arg_spec->{'x.name.is_plural'}) {
201 2 100       7 if ($arg_spec->{'x.name.singular'}) {
202 1         2 $singular_opt = _arg2opt($arg_spec->{'x.name.singular'});
203             } else {
204 1         1015 require Lingua::EN::PluralToSingular;
205 1         2140 $singular_opt = Lingua::EN::PluralToSingular::to_singular($opt);
206             }
207             } else {
208 52         145 $singular_opt = $opt;
209             }
210 54         137 push @opts , $singular_opt;
211 54         88 push @types, $eltype;
212 54 100       120 push @isaos, $is_array_of_simple ? 1:0;
213 54 100       111 push @ishos, $is_hash_of_simple ? 1:0;
214             }
215              
216 234 100 100     668 if ($is_simple || !@opts) {
217 180         331 push @opts , $opt;
218 180         270 push @types, $type;
219 180         249 push @isaos, 0;
220 180         264 push @ishos, 0;
221             }
222              
223 234         334 my @res;
224              
225 234         598 for my $i (0..$#opts) {
226 234         447 my $opt = $opts[$i];
227 234         382 my $type = $types[$i];
228 234         341 my $isaos = $isaos[$i];
229 234         345 my $ishos = $ishos[$i];
230              
231 234 100       613 if ($type eq 'bool') {
    100          
232 22 100 66     196 if (length $opt == 1) {
    100 33        
    100 66        
      33        
233             # single-letter option like -b doesn't get --nob.
234 4         70 push @res, ($opt, {opts=>[$opt]}), undef;
235             } elsif ($cset->{is} || $cset->{is_true}) {
236             # an always-true bool ('true' or [bool => {is=>1}] or
237             # [bool=>{is_true=>1}] also means it's a flag and should not get
238             # --nofoo.
239 2         11 push @res, ($opt, {opts=>[$opt]}), undef;
240             } elsif ((defined $cset->{is} && !$cset->{is}) ||
241             (defined $cset->{is_true} && !$cset->{is_true})) {
242             # an always-false bool ('false' or [bool => {is=>0}] or
243             # [bool=>{is_true=>0}] also means it's a flag and should only be
244             # getting --nofoo.
245 1         4 for (negations_for_option($opt)) {
246 2         43 push @res, $_, {opts=>[$_]}, {is_neg=>1, pos_opts=>[$opt]};
247             }
248             } else {
249             # a regular bool gets --foo as well as --nofoo
250 15         69 my @negs = negations_for_option($opt);
251 15         328 push @res, $opt, {opts=>[$opt]}, {is_neg=>0, neg_opts=>\@negs};
252 15         40 for (@negs) {
253 29         138 push @res, $_, {opts=>[$_]}, {is_neg=>1, pos_opts=>[$opt]};
254             }
255             }
256             } elsif ($type eq 'buf') {
257 3         61 push @res, (
258             "$opt=s", {opts=>[$opt], desttype=>"", type=>"s"}, undef,
259             "$opt-base64=s", {opts=>["$opt-base64"], desttype=>"", type=>"s"}, {is_base64=>1},
260             );
261             } else {
262 209 100       747 my $t = ($type eq 'int' ? 's' : $type eq 'float' ? 's' : 's') .
    100          
    100          
    100          
263             ($isaos ? '@' : $ishos ? '%' : '');
264 209         1095 push @res, ("$opt=$t", {opts=>[$opt], desttype=>"", type=>$t}, undef);
265             }
266             }
267              
268 234         869 @res;
269             }
270              
271             sub _args2opts {
272 81     81   557 my %args = @_;
273              
274 81         193 my $argprefix = $args{argprefix};
275 81         159 my $parent_args = $args{parent_args};
276 81         134 my $meta = $args{meta};
277 81         136 my $seen_opts = $args{seen_opts};
278 81         129 my $seen_common_opts = $args{seen_common_opts};
279 81         132 my $seen_func_opts = $args{seen_func_opts};
280 81         141 my $rargs = $args{rargs};
281 81         128 my $go_spec = $args{go_spec};
282 81         146 my $specmeta = $args{specmeta};
283              
284 81   50     198 my $args_prop = $meta->{args} // {};
285              
286 81         281 for my $arg (keys %$args_prop) {
287 208         558 my $fqarg = "$argprefix$arg";
288 208         350 my $arg_spec = $args_prop->{$arg};
289 0 0       0 next if grep { $_ eq 'hidden' || $_ eq 'hidden-cli' }
290 208 50 50     315 @{ $arg_spec->{tags} // [] };
  208         967  
291 208   50     550 my $sch = $arg_spec->{schema} // ['any', {}];
292 208         461 my ($is_simple, $is_array_of_simple, $is_hash_of_simple, $type, $cset, $eltype) =
293             _is_simple_or_array_of_simple_or_hash_of_simple($sch);
294              
295             # XXX normalization of 'of' clause should've been handled by sah itself
296 208 100 100     652 if ($type eq 'array' && $cset->{of}) {
297 25         92 $cset->{of} = normalize_schema($cset->{of});
298             }
299 208         860 my $opt = _arg2opt($fqarg);
300 208 100       596 if ($seen_opts->{$opt}) {
301 3         5 my $i = 1;
302 3         6 my $opt2;
303 3         6 while (1) {
304 3 50       13 $opt2 = "$opt-arg" . ($i > 1 ? $i : '');
305 3 50       12 last unless $seen_opts->{$opt2};
306 0         0 $i++;
307             }
308 3         8 $opt = $opt2;
309             }
310              
311 208         356 my $stash = {};
312              
313             # why we use coderefs here? due to Getopt::Long's behavior. when
314             # @ARGV=qw() and go_spec is ('foo=s' => \$opts{foo}) then %opts will
315             # become (foo=>undef). but if go_spec is ('foo=s' => sub { $opts{foo} =
316             # $_[1] }) then %opts will become (), which is what we prefer, so we can
317             # later differentiate "unspecified" (exists($opts{foo}) == false) and
318             # "specified as undef" (exists($opts{foo}) == true but
319             # defined($opts{foo}) == false).
320              
321             my $handler = sub {
322 82     82   25326 my ($val, $val_set);
323              
324             # how many times have been called for this argument?
325 82         249 my $num_called = ++$stash->{called}{$arg};
326              
327             # hashify rargs till the end of the handler scope if it happens to
328             # be an array (this is the case when we want to fill values using
329             # element_meta).
330 82         120 my $rargs = do {
331 82 100       216 if (ref($rargs) eq 'ARRAY') {
332 5   100     86 $rargs->[$num_called-1] //= {};
333 5         15 $rargs->[$num_called-1];
334             } else {
335 77         147 $rargs;
336             }
337             };
338              
339 82 100       191 if ($is_simple) {
    100          
    100          
340 54         86 $val_set = 1; $val = $_[1];
  54         111  
341 54         120 $rargs->{$arg} = $val;
342             } elsif ($is_array_of_simple) {
343 16   100     70 $rargs->{$arg} //= [];
344 16         25 $val_set = 1; $val = $_[1];
  16         27  
345 16         28 push @{ $rargs->{$arg} }, $val;
  16         39  
346             } elsif ($is_hash_of_simple) {
347 7   100     32 $rargs->{$arg} //= {};
348 7         14 $val_set = 1; $val = $_[2];
  7         14  
349 7         17 $rargs->{$arg}{$_[1]} = $val;
350             } else {
351             {
352 5         9 my ($success, $e, $decoded);
  5         11  
353 5         35 ($success, $e, $decoded) = _parse_json($_[1]);
354 5 100       17 if ($success) {
355 3         5 $val_set = 1; $val = $decoded;
  3         7  
356 3         8 $rargs->{$arg} = $val;
357 3         7 last;
358             }
359 2         9 ($success, $e, $decoded) = _parse_yaml($_[1]);
360 2 100       8 if ($success) {
361 1         3 $val_set = 1; $val = $decoded;
  1         2  
362 1         3 $rargs->{$arg} = $val;
363 1         3 last;
364             }
365 1         11 die "Invalid YAML/JSON in arg '$fqarg'";
366             }
367             }
368 81 100 66     451 if ($val_set && $arg_spec->{cmdline_on_getopt}) {
369 5         18 $arg_spec->{cmdline_on_getopt}->(
370             arg=>$arg, fqarg=>$fqarg, value=>$val, args=>$rargs,
371             opt=>$opt,
372             );
373             }
374 208         1265 }; # handler
375              
376 208         515 my @triplets = _opt2ospec($opt, $sch, $arg_spec);
377 208         350 my $aliases_processed;
378 208         708 while (my ($ospec, $parsed, $extra) = splice @triplets, 0, 3) {
379 241   100     894 $extra //= {};
380 241 100       779 if ($extra->{is_neg}) {
    100          
    100          
381 31     2   106 $go_spec->{$ospec} = sub { $handler->($_[0], 0) };
  2         1109  
382             } elsif (defined $extra->{is_neg}) {
383 15     2   70 $go_spec->{$ospec} = sub { $handler->($_[0], 1) };
  2         510  
384             } elsif ($extra->{is_base64}) {
385             $go_spec->{$ospec} = sub {
386 1     1   1007 require MIME::Base64;
387 1         764 my $decoded = MIME::Base64::decode($_[1]);
388 1         4 $handler->($_[0], $decoded);
389 3         15 };
390             } else {
391 192         422 $go_spec->{$ospec} = $handler;
392             }
393              
394 241         1015 $specmeta->{$ospec} = {arg=>$arg, fqarg=>$fqarg, parsed=>$parsed, %$extra};
395 241         428 for (@{ $parsed->{opts} }) {
  241         553  
396 241         504 $seen_opts->{$_}++; $seen_func_opts->{$_} = $fqarg;
  241         635  
397             }
398              
399 241 100 100     649 if ($parent_args->{per_arg_json} && !$is_simple) {
400 8         20 my $jopt = "$opt-json";
401 8 50       22 if ($seen_opts->{$jopt}) {
402 0         0 warn "Clash of option: $jopt, not added";
403             } else {
404 8         20 my $jospec = "$jopt=s";
405 8         27 my $parsed = {type=>"s", opts=>[$jopt]};
406             $go_spec->{$jospec} = sub {
407 1     1   330 my ($success, $e, $decoded);
408 1         5 ($success, $e, $decoded) = _parse_json($_[1]);
409 1 50       5 if ($success) {
410 1         4 $rargs->{$arg} = $decoded;
411             } else {
412 0         0 die "Invalid JSON in option --$jopt: $_[1]: $e";
413             }
414 8         78 };
415 8         40 $specmeta->{$jospec} = {arg=>$arg, fqarg=>$fqarg, is_json=>1, parsed=>$parsed, %$extra};
416 8         21 $seen_opts->{$jopt}++; $seen_func_opts->{$jopt} = $fqarg;
  8         22  
417             }
418             }
419 241 100 100     559 if ($parent_args->{per_arg_yaml} && !$is_simple) {
420 8         23 my $yopt = "$opt-yaml";
421 8 50       29 if ($seen_opts->{$yopt}) {
422 0         0 warn "Clash of option: $yopt, not added";
423             } else {
424 8         22 my $yospec = "$yopt=s";
425 8         25 my $parsed = {type=>"s", opts=>[$yopt]};
426             $go_spec->{$yospec} = sub {
427 1     1   389 my ($success, $e, $decoded);
428 1         7 ($success, $e, $decoded) = _parse_yaml($_[1]);
429 1 50       8 if ($success) {
430 1         4 $rargs->{$arg} = $decoded;
431             } else {
432 0         0 die "Invalid YAML in option --$yopt: $_[1]: $e";
433             }
434 8         40 };
435 8         35 $specmeta->{$yospec} = {arg=>$arg, fqarg=>$fqarg, is_yaml=>1, parsed=>$parsed, %$extra};
436 8         19 $seen_opts->{$yopt}++; $seen_func_opts->{$yopt} = $fqarg;
  8         21  
437             }
438             }
439              
440             # parse argv_aliases
441 241 100 100     677 if ($arg_spec->{cmdline_aliases} && !$aliases_processed++) {
442 31         50 for my $al (keys %{$arg_spec->{cmdline_aliases}}) {
  31         107  
443 35         77 my $alspec = $arg_spec->{cmdline_aliases}{$al};
444             my $alsch = $alspec->{schema} //
445 35 100 66     147 $alspec->{is_flag} ? [bool=>{req=>1,is=>1}] : $sch;
446 35         62 my $altype = $alsch->[0];
447 35         106 my $alopt = _arg2opt("$argprefix$al");
448 35 50       103 if ($seen_opts->{$alopt}) {
449 0         0 warn "Clash of cmdline_alias option $al";
450 0         0 next;
451             }
452 35         63 my $alcode = $alspec->{code};
453 35         61 my $alospec;
454             my $parsed;
455 35 100 100     124 if ($alcode && $alsch->[0] eq 'bool') {
456             # bool --alias doesn't get --noalias if has code
457 9         22 $alospec = $alopt; # instead of "$alopt!"
458 9         32 $parsed = {opts=>[$alopt]};
459             } else {
460 26         58 ($alospec, $parsed) = _opt2ospec($alopt, $alsch);
461             }
462              
463 35 100       89 if ($alcode) {
464 12 100       42 if ($alcode eq 'CODE') {
465 3 100       8 if ($parent_args->{ignore_converted_code}) {
466 1     1   6 $alcode = sub {};
467             } else {
468             return [
469 2         22 501,
470             join("",
471             "Code in cmdline_aliases for arg $fqarg ",
472             "got converted into string, probably ",
473             "because of JSON/YAML transport"),
474             ];
475             }
476             }
477             # alias handler
478             $go_spec->{$alospec} = sub {
479              
480             # do the same like in arg handler
481 3     3   1449 my $num_called = ++$stash->{called}{$arg};
482 3         7 my $rargs = do {
483 3 50       12 if (ref($rargs) eq 'ARRAY') {
484 0   0     0 $rargs->[$num_called-1] //= {};
485 0         0 $rargs->[$num_called-1];
486             } else {
487 3         6 $rargs;
488             }
489             };
490              
491 3         11 $alcode->($rargs, $_[1]);
492 10         60 };
493             } else {
494 23         61 $go_spec->{$alospec} = $handler;
495             }
496             $specmeta->{$alospec} = {
497 33 100       275 (summary => $alspec->{summary}) x !!defined($alspec->{summary}),
498             alias => $al,
499             is_alias => 1,
500             alias_for => $ospec,
501             arg => $arg,
502             fqarg => $fqarg,
503             is_code => $alcode ? 1:0,
504             parsed => $parsed,
505             %$extra,
506             };
507 33 100       93 push @{$specmeta->{$ospec}{($alcode ? '':'non').'code_aliases'}},
  33         152  
508             $alospec;
509 33         83 $seen_opts->{$alopt}++; $seen_func_opts->{$alopt} = $fqarg;
  33         94  
510             }
511             } # cmdline_aliases
512              
513             # submetadata
514 239 100       497 if ($arg_spec->{meta}) {
515 2         7 $rargs->{$arg} = {};
516             my $res = _args2opts(
517             %args,
518             argprefix => "$argprefix$arg\::",
519             meta => $arg_spec->{meta},
520 2         49 rargs => $rargs->{$arg},
521             );
522 2 50       10 return $res if $res;
523             }
524              
525             # element submetadata
526 239 100       1213 if ($arg_spec->{element_meta}) {
527 3         9 $rargs->{$arg} = [];
528             my $res = _args2opts(
529             %args,
530             argprefix => "$argprefix$arg\::",
531             meta => $arg_spec->{element_meta},
532 3         20 rargs => $rargs->{$arg},
533             );
534 3 50       18 return $res if $res;
535             }
536             } # for ospec triplet
537              
538             } # for arg
539              
540 79         302 undef;
541             }
542              
543             $SPEC{gen_getopt_long_spec_from_meta} = {
544             v => 1.1,
545             summary => 'Generate Getopt::Long spec from Rinci function metadata',
546             description => <<'_',
547              
548             This routine will produce a <pm:Getopt::Long> specification from Rinci function
549             metadata, as well as some more data structure in the result metadata to help
550             producing a command-line help/usage message.
551              
552             Function arguments will be mapped to command-line options with the same name,
553             with non-alphanumeric characters changed to `-` (`-` is preferred over `_`
554             because it lets user avoid pressing Shift on popular keyboards). For example:
555             `file_size` becomes `file-size`, `file_size.max` becomes `file-size-max`. If
556             function argument option name clashes with command-line option or another
557             existing option, it will be renamed to `NAME-arg` (or `NAME-arg2` and so on).
558             For example: `help` will become `help-arg` (if `common_opts` contains `help`,
559             that is).
560              
561             Each command-line alias (`cmdline_aliases` property) in the argument
562             specification will also be added as command-line option, except if it clashes
563             with an existing option, in which case this function will warn and skip adding
564             the alias. For more information about `cmdline_aliases`, see `Rinci::function`.
565              
566             For arguments with type of `bool`, Getopt::Long will by default also
567             automatically recognize `--noNAME` or `--no-NAME` in addition to `--name`. So
568             this function will also check those names for clashes.
569              
570             For arguments with type array of simple scalar, `--NAME` can be specified more
571             than once to append to the array.
572              
573             If `per_arg_json` setting is active, and argument's schema is not a "required
574             simple scalar" (e.g. an array, or a nullable string), then `--NAME-json` will
575             also be added to let users input undef (through `--NAME-json null`) or a
576             non-scalar value (e.g. `--NAME-json '[1,2,3]'`). If this name conflicts with
577             another existing option, a warning will be displayed and the option will not be
578             added.
579              
580             If `per_arg_yaml` setting is active, and argument's schema is not a "required
581             simple scalar" (e.g. an array, or a nullable string), then `--NAME-yaml` will
582             also be added to let users input undef (through `--NAME-yaml '~'`) or a
583             non-scalar value (e.g. `--NAME-yaml '[foo, bar]'`). If this name conflicts with
584             another existing option, a warning will be displayed and the option will not be
585             added. YAML can express a larger set of values, e.g. binary data, circular
586             references, etc.
587              
588             Will produce a hash (Getopt::Long spec), with `func.specmeta`, `func.opts`,
589             `func.common_opts`, `func.func_opts` that contain extra information
590             (`func.specmeta` is a hash of getopt spec name and a hash of extra information
591             while `func.*opts` lists all used option names).
592              
593             _
594             args => {
595             meta => {
596             summary => 'Rinci function metadata',
597             schema => 'hash*',
598             req => 1,
599             },
600             meta_is_normalized => {
601             schema => 'bool*',
602             },
603             args => {
604             summary => 'Reference to hash which will store the result',
605             schema => 'hash*',
606             },
607             common_opts => {
608             summary => 'Common options',
609             description => <<'_',
610              
611             A hash where the values are hashes containing these keys: `getopt` (Getopt::Long
612             option specification), `handler` (Getopt::Long handler). Will be passed to
613             `get_args_from_argv()`. Example:
614              
615             {
616             help => {
617             getopt => 'help|h|?',
618             handler => sub { ... },
619             summary => 'Display help and exit',
620             },
621             version => {
622             getopt => 'version|v',
623             handler => sub { ... },
624             summary => 'Display version and exit',
625             },
626             }
627              
628             _
629             schema => ['hash*'],
630             },
631             per_arg_json => {
632             summary => 'Whether to add --NAME-json for non-simple arguments',
633             schema => 'bool',
634             default => 0,
635             description => <<'_',
636              
637             Will also interpret command-line arguments as JSON if assigned to function
638             arguments, if arguments' schema is not simple scalar.
639              
640             _
641             },
642             per_arg_yaml => {
643             summary => 'Whether to add --NAME-yaml for non-simple arguments',
644             schema => 'bool',
645             default => 0,
646             description => <<'_',
647              
648             Will also interpret command-line arguments as YAML if assigned to function
649             arguments, if arguments' schema is not simple scalar.
650              
651             _
652             },
653             ignore_converted_code => {
654             summary => 'Whether to ignore coderefs converted to string',
655             schema => 'bool',
656             default => 0,
657             description => <<'_',
658              
659             Across network through JSON encoding, coderef in metadata (e.g. in
660             `cmdline_aliases` property) usually gets converted to string `CODE`. In some
661             cases, like for tab completion, this is pretty harmless so you can turn this
662             option on. For example, in the case of `cmdline_aliases`, the effect is just
663             that command-line aliases code are not getting executed, but this is usually
664             okay.
665              
666             _
667             },
668             },
669             };
670             sub gen_getopt_long_spec_from_meta {
671 76     76 1 576 my %fargs = @_;
672              
673 76 50       236 my $meta = $fargs{meta} or return [400, "Please specify meta"];
674 76 100       198 unless ($fargs{meta_is_normalized}) {
675 1         501 require Perinci::Sub::Normalize;
676 1         1213 $meta = Perinci::Sub::Normalize::normalize_function_metadata($meta);
677             }
678 76   50     4417 my $co = $fargs{common_opts} // {};
679 76   50     196 my $per_arg_yaml = $fargs{per_arg_yaml} // 0;
680 76   50     167 my $per_arg_json = $fargs{per_arg_json} // 0;
681 76         132 my $ignore_converted_code = $fargs{ignore_converted_code};
682 76   100     166 my $rargs = $fargs{args} // {};
683              
684 76         329 my %go_spec;
685             my %specmeta; # key = option spec, val = hash of extra info
686 76         0 my %seen_opts;
687 76         0 my %seen_common_opts;
688 76         0 my %seen_func_opts;
689              
690 76         256 for my $k (keys %$co) {
691 9         18 my $v = $co->{$k};
692 9         15 my $ospec = $v->{getopt};
693 9         19 my $handler = $v->{handler};
694 9 50       43 my $res = parse_getopt_long_opt_spec($ospec)
695             or return [400, "Can't parse common opt spec '$ospec'"];
696 9         489 $go_spec{$ospec} = $handler;
697 9         43 $specmeta{$ospec} = {common_opt=>$k, arg=>undef, parsed=>$res};
698 9         18 for (@{ $res->{opts} }) {
  9         25  
699 12 50       37 return [412, "Clash of common opt '$_'"] if $seen_opts{$_};
700 12         30 $seen_opts{$_}++; $seen_common_opts{$_} = $ospec;
  12         20  
701 12 100       35 if ($res->{is_neg}) {
702 1         5 $seen_opts{"no$_"}++ ; $seen_common_opts{"no$_"} = $ospec;
  1         3  
703 1         2 $seen_opts{"no-$_"}++; $seen_common_opts{"no-$_"} = $ospec;
  1         5  
704             }
705             }
706             }
707              
708 76         328 my $res = _args2opts(
709             argprefix => "",
710             parent_args => \%fargs,
711             meta => $meta,
712             seen_opts => \%seen_opts,
713             seen_common_opts => \%seen_common_opts,
714             seen_func_opts => \%seen_func_opts,
715             rargs => $rargs,
716             go_spec => \%go_spec,
717             specmeta => \%specmeta,
718             );
719 76 100       224 return $res if $res;
720              
721 74 100       244 my $opts = [sort(map {length($_)>1 ? "--$_":"-$_"} keys %seen_opts)];
  302         1055  
722 74 100       282 my $common_opts = [sort(map {length($_)>1 ? "--$_":"-$_"} keys %seen_common_opts)];
  14         41  
723 74 100       180 my $func_opts = [sort(map {length($_)>1 ? "--$_":"-$_"} keys %seen_func_opts)];
  288         739  
724 74         195 my $opts_by_common = {};
725 74         185 for my $k (keys %$co) {
726 9         20 my $v = $co->{$k};
727 9         15 my $ospec = $v->{getopt};
728 9         14 my @opts;
729 9         33 for (keys %seen_common_opts) {
730 58 100       117 next unless $seen_common_opts{$_} eq $ospec;
731 14 100       40 push @opts, (length($_)>1 ? "--$_":"-$_");
732             }
733 9         33 $opts_by_common->{$ospec} = [sort @opts];
734             }
735              
736 74         148 my $opts_by_arg = {};
737 74         179 for (keys %seen_func_opts) {
738 288         467 my $fqarg = $seen_func_opts{$_};
739 288 100       386 push @{ $opts_by_arg->{$fqarg} }, length($_)>1 ? "--$_":"-$_";
  288         893  
740             }
741 74         230 for (keys %$opts_by_arg) {
742 206         298 $opts_by_arg->{$_} = [sort @{ $opts_by_arg->{$_} }];
  206         573  
743             }
744              
745 74         787 [200, "OK", \%go_spec,
746             {
747             "func.specmeta" => \%specmeta,
748             "func.opts" => $opts,
749             "func.common_opts" => $common_opts,
750             "func.func_opts" => $func_opts,
751             "func.opts_by_arg" => $opts_by_arg,
752             "func.opts_by_common" => $opts_by_common,
753             }];
754             }
755              
756             $SPEC{get_args_from_argv} = {
757             v => 1.1,
758             summary => 'Get subroutine arguments (%args) from command-line arguments '.
759             '(@ARGV)',
760             description => <<'_',
761              
762             Using information in Rinci function metadata's `args` property, parse command
763             line arguments `@argv` into hash `%args`, suitable for passing into subroutines.
764              
765             Currently uses <pm:Getopt::Long>'s `GetOptions` to do the parsing.
766              
767             As with GetOptions, this function modifies its `argv` argument, so you might
768             want to copy the original `argv` first (or pass a copy instead) if you want to
769             preserve the original.
770              
771             See also: gen_getopt_long_spec_from_meta() which is the routine that generates
772             the specification.
773              
774             _
775             args => {
776             argv => {
777             schema => ['array*' => {
778             of => 'str*',
779             }],
780             description => 'If not specified, defaults to @ARGV',
781             },
782             args => {
783             summary => 'Specify input args, with some arguments preset',
784             schema => ['hash'],
785             },
786             meta => {
787             schema => ['hash*' => {}],
788             req => 1,
789             },
790             meta_is_normalized => {
791             summary => 'Can be set to 1 if your metadata is normalized, '.
792             'to avoid duplicate effort',
793             schema => 'bool',
794             default => 0,
795             },
796             strict => {
797             schema => ['bool' => {default=>1}],
798             summary => 'Strict mode',
799             description => <<'_',
800              
801             If set to 0, will still return parsed argv even if there are parsing errors
802             (reported by Getopt::Long). If set to 1 (the default), will die upon error.
803              
804             Normally you would want to use strict mode, for more error checking. Setting off
805             strict is used by, for example, Perinci::Sub::Complete during completion where
806             the command-line might still be incomplete.
807              
808             Should probably be named `ignore_errors` or `allow_unknown_options`. :-)
809              
810             _
811             },
812             per_arg_yaml => {
813             schema => ['bool' => {default=>0}],
814             summary => 'Whether to recognize --ARGNAME-yaml',
815             description => <<'_',
816              
817             This is useful for example if you want to specify a value which is not
818             expressible from the command-line, like 'undef'.
819              
820             % script.pl --name-yaml '~'
821              
822             See also: per_arg_json. You should enable just one instead of turning on both.
823              
824             _
825             },
826             per_arg_json => {
827             schema => ['bool' => {default=>0}],
828             summary => 'Whether to recognize --ARGNAME-json',
829             description => <<'_',
830              
831             This is useful for example if you want to specify a value which is not
832             expressible from the command-line, like 'undef'.
833              
834             % script.pl --name-json 'null'
835              
836             But every other string will need to be quoted:
837              
838             % script.pl --name-json '"foo"'
839              
840             See also: per_arg_yaml. You should enable just one instead of turning on both.
841              
842             _
843             },
844             common_opts => {
845             summary => 'Common options',
846             description => <<'_',
847              
848             A hash where the values are hashes containing these keys: `getopt` (Getopt::Long
849             option specification), `handler` (Getopt::Long handler). Will be passed to
850             `get_args_from_argv()`. Example:
851              
852             {
853             help => {
854             getopt => 'help|h|?',
855             handler => sub { ... },
856             summary => 'Display help and exit',
857             },
858             version => {
859             getopt => 'version|v',
860             handler => sub { ... },
861             summary => 'Display version and exit',
862             },
863             }
864              
865             _
866             schema => ['hash*'],
867             },
868             allow_extra_elems => {
869             schema => ['bool' => {default=>0}],
870             summary => 'Allow extra/unassigned elements in argv',
871             description => <<'_',
872              
873             If set to 1, then if there are array elements unassigned to one of the
874             arguments, instead of generating an error, this function will just ignore them.
875              
876             This option will be passed to Perinci::Sub::GetArgs::Array's allow_extra_elems.
877              
878             _
879             },
880             on_missing_required_args => {
881             schema => 'code',
882             summary => 'Execute code when there is missing required args',
883             description => <<'_',
884              
885             This can be used to give a chance to supply argument value from other sources if
886             not specified by command-line options. Perinci::CmdLine, for example, uses this
887             hook to supply value from STDIN or file contents (if argument has `cmdline_src`
888             specification key set).
889              
890             This hook will be called for each missing argument. It will be supplied hash
891             arguments: (arg => $the_missing_argument_name, args =>
892             $the_resulting_args_so_far, spec => $the_arg_spec).
893              
894             The hook can return true if it succeeds in making the missing situation
895             resolved. In this case, this function will not report the argument as missing.
896              
897             _
898             },
899             ignore_converted_code => {
900             summary => 'Whether to ignore coderefs converted to string',
901             schema => 'bool',
902             default => 0,
903             description => <<'_',
904              
905             Across network through JSON encoding, coderef in metadata (e.g. in
906             `cmdline_aliases` property) usually gets converted to string `CODE`. In some
907             cases, like for tab completion, this is harmless so you can turn this option on.
908              
909             _
910             },
911             ggls_res => {
912             summary => 'Full result from gen_getopt_long_spec_from_meta()',
913             schema => 'array*', # XXX envres
914             description => <<'_',
915              
916             If you already call `gen_getopt_long_spec_from_meta()`, you can pass the _full_ enveloped result
917             here, to avoid calculating twice.
918              
919             _
920             tags => ['category:optimization'],
921             },
922             },
923             result => {
924             description => <<'_',
925              
926             Error codes:
927              
928             * 400 - Error in Getopt::Long option specification, e.g. in common_opts.
929              
930             * 500 - failure in GetOptions, meaning argv is not valid according to metadata
931             specification (only if 'strict' mode is enabled).
932              
933             * 501 - coderef in cmdline_aliases got converted into a string, probably because
934             the metadata was transported (e.g. through Riap::HTTP/Riap::Simple).
935              
936             _
937             },
938             };
939             sub get_args_from_argv {
940 75     75 1 400512 require Getopt::Long;
941              
942 75         11501 my %fargs = @_;
943 75   100     306 my $argv = $fargs{argv} // \@ARGV;
944 75 50       277 my $meta = $fargs{meta} or return [400, "Please specify meta"];
945 75 50       207 unless ($fargs{meta_is_normalized}) {
946 75         939 require Perinci::Sub::Normalize;
947 75         1531 $meta = Perinci::Sub::Normalize::normalize_function_metadata($meta);
948             }
949 75   100     28677 my $strict = $fargs{strict} // 1;
950 75   100     345 my $common_opts = $fargs{common_opts} // {};
951 75   100     281 my $per_arg_yaml = $fargs{per_arg_yaml} // 0;
952 75   100     259 my $per_arg_json = $fargs{per_arg_json} // 0;
953 75   100     265 my $allow_extra_elems = $fargs{allow_extra_elems} // 0;
954 75         160 my $on_missing = $fargs{on_missing_required_args};
955 75         131 my $ignore_converted_code = $fargs{ignore_converted_code};
956             #$log->tracef("-> get_args_from_argv(), argv=%s", $argv);
957              
958             # to store the resulting args
959 75   100     279 my $rargs = $fargs{args} // {};
960              
961             # 1. first we generate Getopt::Long spec
962 75   33     352 my $genres = $fargs{ggls_res} // gen_getopt_long_spec_from_meta(
963             meta => $meta, meta_is_normalized => 1,
964             args => $rargs,
965             common_opts => $common_opts,
966             per_arg_json => $per_arg_json,
967             per_arg_yaml => $per_arg_yaml,
968             ignore_converted_code => $ignore_converted_code,
969             );
970 75 100       244 return err($genres->[0], "Can't generate Getopt::Long spec", $genres)
971             if $genres->[0] != 200;
972 73         141 my $go_spec = $genres->[2];
973              
974             # 2. then we run GetOptions to fill $rargs from command-line opts
975             #$log->tracef("GetOptions spec: %s", \@go_spec);
976             {
977 73 100   0   111 local $SIG{__WARN__} = sub{} if !$strict;
  73         162  
978 73 100       344 my $old_go_conf = Getopt::Long::Configure(
979             $strict ? "no_pass_through" : "pass_through",
980             "no_ignore_case", "permute", "no_getopt_compat", "gnu_compat", "bundling");
981 73         7510 my $res = Getopt::Long::GetOptionsFromArray($argv, %$go_spec);
982 73         9009 Getopt::Long::Configure($old_go_conf);
983 73 100       1532 unless ($res) {
984 9 50       302 return [500, "GetOptions failed"] if $strict;
985             }
986             }
987              
988             # 3. then we try to fill $rargs from remaining command-line arguments (for
989             # args which have 'pos' spec specified)
990              
991 64         143 my $args_prop = $meta->{args};
992              
993 64 100       176 if (@$argv) {
994 13         77 my $res = get_args_from_array(
995             array=>$argv, meta => $meta,
996             meta_is_normalized => 1,
997             allow_extra_elems => $allow_extra_elems,
998             );
999 13 100 100     765 if ($res->[0] != 200 && $strict) {
    50 66        
    100          
1000 2         12 return err(500, "Get args from array failed", $res);
1001             } elsif ($strict && $res->[0] != 200) {
1002 0         0 return err("Can't get args from argv", $res);
1003             } elsif ($res->[0] == 200) {
1004 10         22 my $pos_args = $res->[2];
1005 10         35 for my $name (keys %$pos_args) {
1006 11         24 my $arg_spec = $args_prop->{$name};
1007 11         22 my $val = $pos_args->{$name};
1008 11 100       34 if (exists $rargs->{$name}) {
1009             return [400, "You specified option --$name but also ".
1010 2 50       97 "argument #".$arg_spec->{pos}] if $strict;
1011             }
1012             my ($is_simple, $is_array_of_simple, $is_hash_of_simple, $type, $cset, $eltype) =
1013 9         25 _is_simple_or_array_of_simple_or_hash_of_simple($arg_spec->{schema});
1014              
1015 9 100 66     71 if (($arg_spec->{slurpy} // $arg_spec->{greedy}) && ref($val) eq 'ARRAY' &&
      66        
      100        
      66        
1016             !$is_array_of_simple && !$is_hash_of_simple) {
1017 1         3 my $i = 0;
1018 1         4 for (@$val) {
1019             TRY_PARSING_AS_JSON_YAML:
1020             {
1021 1         3 my ($success, $e, $decoded);
  1         3  
1022 1 50       5 if ($per_arg_json) {
1023 1         5 ($success, $e, $decoded) = _parse_json($_);
1024 1 50       5 if ($success) {
1025 0         0 $_ = $decoded;
1026 0         0 last TRY_PARSING_AS_JSON_YAML;
1027             } else {
1028 1         46 warn "Failed trying to parse argv #$i as JSON: $e";
1029             }
1030             }
1031 1 50       8 if ($per_arg_yaml) {
1032 1         6 ($success, $e, $decoded) = _parse_yaml($_);
1033 1 50       5 if ($success) {
1034 1         3 $_ = $decoded;
1035 1         4 last TRY_PARSING_AS_JSON_YAML;
1036             } else {
1037 0         0 warn "Failed trying to parse argv #$i as YAML: $e";
1038             }
1039             }
1040             }
1041 1         4 $i++;
1042             }
1043             }
1044 9 100 66     54 if (!($arg_spec->{slurpy} // $arg_spec->{greedy}) && !$is_simple) {
      100        
1045             TRY_PARSING_AS_JSON_YAML:
1046             {
1047 1         2 my ($success, $e, $decoded);
  1         4  
1048 1 50       5 if ($per_arg_json) {
1049 1         4 ($success, $e, $decoded) = _parse_json($val);
1050 1 50       5 if ($success) {
1051 0         0 $val = $decoded;
1052 0         0 last TRY_PARSING_AS_JSON_YAML;
1053             } else {
1054 1         45 warn "Failed trying to parse argv #$arg_spec->{pos} as JSON: $e";
1055             }
1056             }
1057 1 50       7 if ($per_arg_yaml) {
1058 1         5 ($success, $e, $decoded) = _parse_yaml($val);
1059 1 50       4 if ($success) {
1060 1         3 $val = $decoded;
1061 1         4 last TRY_PARSING_AS_JSON_YAML;
1062             } else {
1063 0         0 warn "Failed trying to parse argv #$arg_spec->{pos} as YAML: $e";
1064             }
1065             }
1066             }
1067             }
1068 9         25 $rargs->{$name} = $val;
1069             # we still call cmdline_on_getopt for this
1070 9 100       37 if ($arg_spec->{cmdline_on_getopt}) {
1071 2 50 33     10 if ($arg_spec->{slurpy} // $arg_spec->{greedy}) {
1072             $arg_spec->{cmdline_on_getopt}->(
1073             arg=>$name, fqarg=>$name, value=>$_, args=>$rargs,
1074             opt=>undef, # this marks that value is retrieved from cmdline arg
1075 2         11 ) for @$val;
1076             } else {
1077 0         0 $arg_spec->{cmdline_on_getopt}->(
1078             arg=>$name, fqarg=>$name, value=>$val, args=>$rargs,
1079             opt=>undef, # this marks that value is retrieved from cmdline arg
1080             );
1081             }
1082             }
1083             }
1084             }
1085             }
1086              
1087             # 4. check missing required args
1088              
1089 60         123 my %missing_args;
1090 60         173 for my $arg (keys %$args_prop) {
1091 149         240 my $arg_spec = $args_prop->{$arg};
1092 149 100       350 if (!exists($rargs->{$arg})) {
1093 70 100       182 next unless $arg_spec->{req};
1094             # give a chance to hook to set missing arg
1095 5 100       16 if ($on_missing) {
1096 2 100       8 next if $on_missing->(arg=>$arg, args=>$rargs, spec=>$arg_spec);
1097             }
1098 4 100       29 next if exists $rargs->{$arg};
1099 3         8 $missing_args{$arg} = 1;
1100             }
1101             }
1102              
1103             # 5. check 'deps', currently we only support 'arg' dep type
1104             {
1105 60 100       110 last unless $strict;
  60         134  
1106              
1107 59         147 for my $arg (keys %$args_prop) {
1108 147         226 my $arg_spec = $args_prop->{$arg};
1109 147 100       433 next unless exists $rargs->{$arg};
1110 79 100       196 next unless $arg_spec->{deps};
1111 2         5 my $dep_arg = $arg_spec->{deps}{arg};
1112 2 50       7 next unless $dep_arg;
1113             return [400, "You specify '$arg', but don't specify '$dep_arg' ".
1114             "(upon which '$arg' depends)"]
1115 2 100       34 unless exists $rargs->{$dep_arg};
1116             }
1117             }
1118              
1119             #$log->tracef("<- get_args_from_argv(), args=%s, remaining argv=%s",
1120             # $rargs, $argv);
1121 59         508 [200, "OK", $rargs, {
1122             "func.missing_args" => [sort keys %missing_args],
1123             "func.gen_getopt_long_spec_result" => $genres,
1124             }];
1125             }
1126              
1127             1;
1128             # ABSTRACT: Get subroutine arguments from command line arguments (@ARGV)
1129              
1130             __END__
1131              
1132             =pod
1133              
1134             =encoding UTF-8
1135              
1136             =head1 NAME
1137              
1138             Perinci::Sub::GetArgs::Argv - Get subroutine arguments from command line arguments (@ARGV)
1139              
1140             =head1 VERSION
1141              
1142             This document describes version 0.845 of Perinci::Sub::GetArgs::Argv (from Perl distribution Perinci-Sub-GetArgs-Argv), released on 2020-09-09.
1143              
1144             =head1 SYNOPSIS
1145              
1146             use Perinci::Sub::GetArgs::Argv;
1147              
1148             my $res = get_args_from_argv(argv=>\@ARGV, meta=>$meta, ...);
1149              
1150             =head1 DESCRIPTION
1151              
1152             This module provides C<get_args_from_argv()>, which parses command line
1153             arguments (C<@ARGV>) into subroutine arguments (C<%args>). This module is used
1154             by L<Perinci::CmdLine>. For explanation on how command-line options are
1155             processed, see Perinci::CmdLine's documentation.
1156              
1157             =head1 FUNCTIONS
1158              
1159              
1160             =head2 gen_getopt_long_spec_from_meta
1161              
1162             Usage:
1163              
1164             gen_getopt_long_spec_from_meta(%args) -> [status, msg, payload, meta]
1165              
1166             Generate Getopt::Long spec from Rinci function metadata.
1167              
1168             This routine will produce a L<Getopt::Long> specification from Rinci function
1169             metadata, as well as some more data structure in the result metadata to help
1170             producing a command-line help/usage message.
1171              
1172             Function arguments will be mapped to command-line options with the same name,
1173             with non-alphanumeric characters changed to C<-> (C<-> is preferred over C<_>
1174             because it lets user avoid pressing Shift on popular keyboards). For example:
1175             C<file_size> becomes C<file-size>, C<file_size.max> becomes C<file-size-max>. If
1176             function argument option name clashes with command-line option or another
1177             existing option, it will be renamed to C<NAME-arg> (or C<NAME-arg2> and so on).
1178             For example: C<help> will become C<help-arg> (if C<common_opts> contains C<help>,
1179             that is).
1180              
1181             Each command-line alias (C<cmdline_aliases> property) in the argument
1182             specification will also be added as command-line option, except if it clashes
1183             with an existing option, in which case this function will warn and skip adding
1184             the alias. For more information about C<cmdline_aliases>, see C<Rinci::function>.
1185              
1186             For arguments with type of C<bool>, Getopt::Long will by default also
1187             automatically recognize C<--noNAME> or C<--no-NAME> in addition to C<--name>. So
1188             this function will also check those names for clashes.
1189              
1190             For arguments with type array of simple scalar, C<--NAME> can be specified more
1191             than once to append to the array.
1192              
1193             If C<per_arg_json> setting is active, and argument's schema is not a "required
1194             simple scalar" (e.g. an array, or a nullable string), then C<--NAME-json> will
1195             also be added to let users input undef (through C<--NAME-json null>) or a
1196             non-scalar value (e.g. C<--NAME-json '[1,2,3]'>). If this name conflicts with
1197             another existing option, a warning will be displayed and the option will not be
1198             added.
1199              
1200             If C<per_arg_yaml> setting is active, and argument's schema is not a "required
1201             simple scalar" (e.g. an array, or a nullable string), then C<--NAME-yaml> will
1202             also be added to let users input undef (through C<--NAME-yaml '~'>) or a
1203             non-scalar value (e.g. C<--NAME-yaml '[foo, bar]'>). If this name conflicts with
1204             another existing option, a warning will be displayed and the option will not be
1205             added. YAML can express a larger set of values, e.g. binary data, circular
1206             references, etc.
1207              
1208             Will produce a hash (Getopt::Long spec), with C<func.specmeta>, C<func.opts>,
1209             C<func.common_opts>, C<func.func_opts> that contain extra information
1210             (C<func.specmeta> is a hash of getopt spec name and a hash of extra information
1211             while C<func.*opts> lists all used option names).
1212              
1213             This function is not exported by default, but exportable.
1214              
1215             Arguments ('*' denotes required arguments):
1216              
1217             =over 4
1218              
1219             =item * B<args> => I<hash>
1220              
1221             Reference to hash which will store the result.
1222              
1223             =item * B<common_opts> => I<hash>
1224              
1225             Common options.
1226              
1227             A hash where the values are hashes containing these keys: C<getopt> (Getopt::Long
1228             option specification), C<handler> (Getopt::Long handler). Will be passed to
1229             C<get_args_from_argv()>. Example:
1230              
1231             {
1232             help => {
1233             getopt => 'help|h|?',
1234             handler => sub { ... },
1235             summary => 'Display help and exit',
1236             },
1237             version => {
1238             getopt => 'version|v',
1239             handler => sub { ... },
1240             summary => 'Display version and exit',
1241             },
1242             }
1243              
1244             =item * B<ignore_converted_code> => I<bool> (default: 0)
1245              
1246             Whether to ignore coderefs converted to string.
1247              
1248             Across network through JSON encoding, coderef in metadata (e.g. in
1249             C<cmdline_aliases> property) usually gets converted to string C<CODE>. In some
1250             cases, like for tab completion, this is pretty harmless so you can turn this
1251             option on. For example, in the case of C<cmdline_aliases>, the effect is just
1252             that command-line aliases code are not getting executed, but this is usually
1253             okay.
1254              
1255             =item * B<meta>* => I<hash>
1256              
1257             Rinci function metadata.
1258              
1259             =item * B<meta_is_normalized> => I<bool>
1260              
1261             =item * B<per_arg_json> => I<bool> (default: 0)
1262              
1263             Whether to add --NAME-json for non-simple arguments.
1264              
1265             Will also interpret command-line arguments as JSON if assigned to function
1266             arguments, if arguments' schema is not simple scalar.
1267              
1268             =item * B<per_arg_yaml> => I<bool> (default: 0)
1269              
1270             Whether to add --NAME-yaml for non-simple arguments.
1271              
1272             Will also interpret command-line arguments as YAML if assigned to function
1273             arguments, if arguments' schema is not simple scalar.
1274              
1275              
1276             =back
1277              
1278             Returns an enveloped result (an array).
1279              
1280             First element (status) is an integer containing HTTP status code
1281             (200 means OK, 4xx caller error, 5xx function error). Second element
1282             (msg) is a string containing error message, or 'OK' if status is
1283             200. Third element (payload) is optional, the actual result. Fourth
1284             element (meta) is called result metadata and is optional, a hash
1285             that contains extra information.
1286              
1287             Return value: (any)
1288              
1289              
1290              
1291             =head2 get_args_from_argv
1292              
1293             Usage:
1294              
1295             get_args_from_argv(%args) -> [status, msg, payload, meta]
1296              
1297             Get subroutine arguments (%args) from command-line arguments (@ARGV).
1298              
1299             Using information in Rinci function metadata's C<args> property, parse command
1300             line arguments C<@argv> into hash C<%args>, suitable for passing into subroutines.
1301              
1302             Currently uses L<Getopt::Long>'s C<GetOptions> to do the parsing.
1303              
1304             As with GetOptions, this function modifies its C<argv> argument, so you might
1305             want to copy the original C<argv> first (or pass a copy instead) if you want to
1306             preserve the original.
1307              
1308             See also: gen_getopt_long_spec_from_meta() which is the routine that generates
1309             the specification.
1310              
1311             This function is not exported by default, but exportable.
1312              
1313             Arguments ('*' denotes required arguments):
1314              
1315             =over 4
1316              
1317             =item * B<allow_extra_elems> => I<bool> (default: 0)
1318              
1319             Allow extraE<sol>unassigned elements in argv.
1320              
1321             If set to 1, then if there are array elements unassigned to one of the
1322             arguments, instead of generating an error, this function will just ignore them.
1323              
1324             This option will be passed to Perinci::Sub::GetArgs::Array's allow_extra_elems.
1325              
1326             =item * B<args> => I<hash>
1327              
1328             Specify input args, with some arguments preset.
1329              
1330             =item * B<argv> => I<array[str]>
1331              
1332             If not specified, defaults to @ARGV
1333              
1334             =item * B<common_opts> => I<hash>
1335              
1336             Common options.
1337              
1338             A hash where the values are hashes containing these keys: C<getopt> (Getopt::Long
1339             option specification), C<handler> (Getopt::Long handler). Will be passed to
1340             C<get_args_from_argv()>. Example:
1341              
1342             {
1343             help => {
1344             getopt => 'help|h|?',
1345             handler => sub { ... },
1346             summary => 'Display help and exit',
1347             },
1348             version => {
1349             getopt => 'version|v',
1350             handler => sub { ... },
1351             summary => 'Display version and exit',
1352             },
1353             }
1354              
1355             =item * B<ggls_res> => I<array>
1356              
1357             Full result from gen_getopt_long_spec_from_meta().
1358              
1359             If you already call C<gen_getopt_long_spec_from_meta()>, you can pass the I<full> enveloped result
1360             here, to avoid calculating twice.
1361              
1362             =item * B<ignore_converted_code> => I<bool> (default: 0)
1363              
1364             Whether to ignore coderefs converted to string.
1365              
1366             Across network through JSON encoding, coderef in metadata (e.g. in
1367             C<cmdline_aliases> property) usually gets converted to string C<CODE>. In some
1368             cases, like for tab completion, this is harmless so you can turn this option on.
1369              
1370             =item * B<meta>* => I<hash>
1371              
1372             =item * B<meta_is_normalized> => I<bool> (default: 0)
1373              
1374             Can be set to 1 if your metadata is normalized, to avoid duplicate effort.
1375              
1376             =item * B<on_missing_required_args> => I<code>
1377              
1378             Execute code when there is missing required args.
1379              
1380             This can be used to give a chance to supply argument value from other sources if
1381             not specified by command-line options. Perinci::CmdLine, for example, uses this
1382             hook to supply value from STDIN or file contents (if argument has C<cmdline_src>
1383             specification key set).
1384              
1385             This hook will be called for each missing argument. It will be supplied hash
1386             arguments: (arg => $the_missing_argument_name, args =>
1387             $the_resulting_args_so_far, spec => $the_arg_spec).
1388              
1389             The hook can return true if it succeeds in making the missing situation
1390             resolved. In this case, this function will not report the argument as missing.
1391              
1392             =item * B<per_arg_json> => I<bool> (default: 0)
1393              
1394             Whether to recognize --ARGNAME-json.
1395              
1396             This is useful for example if you want to specify a value which is not
1397             expressible from the command-line, like 'undef'.
1398              
1399             % script.pl --name-json 'null'
1400              
1401             But every other string will need to be quoted:
1402              
1403             % script.pl --name-json '"foo"'
1404              
1405             See also: per_arg_yaml. You should enable just one instead of turning on both.
1406              
1407             =item * B<per_arg_yaml> => I<bool> (default: 0)
1408              
1409             Whether to recognize --ARGNAME-yaml.
1410              
1411             This is useful for example if you want to specify a value which is not
1412             expressible from the command-line, like 'undef'.
1413              
1414             % script.pl --name-yaml '~'
1415              
1416             See also: per_arg_json. You should enable just one instead of turning on both.
1417              
1418             =item * B<strict> => I<bool> (default: 1)
1419              
1420             Strict mode.
1421              
1422             If set to 0, will still return parsed argv even if there are parsing errors
1423             (reported by Getopt::Long). If set to 1 (the default), will die upon error.
1424              
1425             Normally you would want to use strict mode, for more error checking. Setting off
1426             strict is used by, for example, Perinci::Sub::Complete during completion where
1427             the command-line might still be incomplete.
1428              
1429             Should probably be named C<ignore_errors> or C<allow_unknown_options>. :-)
1430              
1431              
1432             =back
1433              
1434             Returns an enveloped result (an array).
1435              
1436             First element (status) is an integer containing HTTP status code
1437             (200 means OK, 4xx caller error, 5xx function error). Second element
1438             (msg) is a string containing error message, or 'OK' if status is
1439             200. Third element (payload) is optional, the actual result. Fourth
1440             element (meta) is called result metadata and is optional, a hash
1441             that contains extra information.
1442              
1443             Return value: (any)
1444              
1445              
1446             Error codes:
1447              
1448             =over
1449              
1450             =item * 400 - Error in Getopt::Long option specification, e.g. in common_opts.
1451              
1452             =item * 500 - failure in GetOptions, meaning argv is not valid according to metadata
1453             specification (only if 'strict' mode is enabled).
1454              
1455             =item * 501 - coderef in cmdline_aliases got converted into a string, probably because
1456             the metadata was transported (e.g. through Riap::HTTP/Riap::Simple).
1457              
1458             =back
1459              
1460             =head1 FAQ
1461              
1462             =head1 HOMEPAGE
1463              
1464             Please visit the project's homepage at L<https://metacpan.org/release/Perinci-Sub-GetArgs-Argv>.
1465              
1466             =head1 SOURCE
1467              
1468             Source repository is at L<https://github.com/perlancar/perl-Perinci-Sub-GetArgs-Argv>.
1469              
1470             =head1 BUGS
1471              
1472             Please report any bugs or feature requests on the bugtracker website L<https://rt.cpan.org/Public/Dist/Display.html?Name=Perinci-Sub-GetArgs-Argv>
1473              
1474             When submitting a bug or request, please include a test-file or a
1475             patch to an existing test-file that illustrates the bug or desired
1476             feature.
1477              
1478             =head1 SEE ALSO
1479              
1480             L<Perinci>
1481              
1482             =head1 AUTHOR
1483              
1484             perlancar <perlancar@cpan.org>
1485              
1486             =head1 COPYRIGHT AND LICENSE
1487              
1488             This software is copyright (c) 2020, 2019, 2017, 2016, 2015, 2014, 2013, 2012, 2011 by perlancar@cpan.org.
1489              
1490             This is free software; you can redistribute it and/or modify it under
1491             the same terms as the Perl 5 programming language system itself.
1492              
1493             =cut