File Coverage

blib/lib/Perinci/Sub/ConvertArgs/Argv.pm
Criterion Covered Total %
statement 65 65 100.0
branch 23 26 88.4
condition 14 21 66.6
subroutine 8 8 100.0
pod 1 1 100.0
total 111 121 91.7


line stmt bran cond sub pod time code
1             package Perinci::Sub::ConvertArgs::Argv;
2              
3             our $DATE = '2016-12-12'; # DATE
4             our $VERSION = '0.10'; # VERSION
5              
6 1     1   23847 use 5.010001;
  1         4  
7 1     1   7 use strict;
  1         3  
  1         36  
8 1     1   8 use warnings;
  1         2  
  1         64  
9              
10 1     1   851 use Data::Sah::Util::Type qw(is_simple);
  1         1380  
  1         91  
11              
12 1     1   8 use Exporter;
  1         2  
  1         1216  
13             our @ISA = qw(Exporter);
14             our @EXPORT_OK = qw(convert_args_to_argv);
15              
16             our %SPEC;
17              
18             sub _json {
19 2     2   21 require JSON;
20 2         40 state $json = JSON->new->allow_nonref;
21 2         31 $json->encode($_[0]);
22             }
23              
24             sub _encode {
25 3 100   3   16 ref($_[0]) ? _json($_[0]) : $_[0];
26             }
27              
28             $SPEC{convert_args_to_argv} = {
29             v => 1.1,
30             summary => 'Convert hash arguments to command-line options (and arguments)',
31             description => <<'_',
32              
33             Convert hash arguments to command-line arguments. This is the reverse of
34             `Perinci::Sub::GetArgs::Argv::get_args_from_argv`.
35              
36             Note: currently the function expects schemas in metadata to be normalized
37             already.
38              
39             _
40             args => {
41             args => {req=>1, schema=>'hash*', pos=>0},
42             meta => {req=>0, schema=>'hash*', pos=>1},
43             use_pos => {
44             summary => 'Whether to use positional arguments',
45             schema => 'bool',
46             description => <<'_',
47              
48             For example, given this metadata:
49              
50             {
51             v => 1.1,
52             args => {
53             arg1 => {pos=>0, req=>1},
54             arg2 => {pos=>1},
55             arg3 => {},
56             },
57             }
58              
59             then under `use_pos=0` the hash `{arg1=>1, arg2=>2, arg3=>'a b'}` will be
60             converted to `['--arg1', 1, '--arg2', 2, '--arg3', 'a b']`. Meanwhile if
61             `use_pos=1` the same hash will be converted to `[1, 2, '--arg3', 'a b']`.
62              
63             _
64             },
65             },
66             };
67             sub convert_args_to_argv {
68 7     7 1 29721 my %fargs = @_;
69              
70 7 50       36 my $iargs = $fargs{args} or return [400, "Please specify args"];
71 7   100     43 my $meta = $fargs{meta} // {v=>1.1};
72 7   100     28 my $args_prop = $meta->{args} // {};
73              
74 7   50     19 my $v = $meta->{v} // 1.0;
75 7 50       24 return [412, "Sorry, only metadata version 1.1 is supported (yours: $v)"]
76             unless $v == 1.1;
77              
78 7         7 my @argv;
79 7         32 my %iargs = %$iargs; # copy 'coz we will delete them one by one as we fill
80              
81 7 100       32 if ($fargs{use_pos}) {
82 2         7 for my $arg (sort {$args_prop->{$a}{pos} <=> $args_prop->{$b}{pos}}
  1         6  
83 5         18 grep {defined $args_prop->{$_}{pos}} keys %iargs) {
84 3         7 my $pos = $args_prop->{$arg}{pos};
85 3 100       10 if ($args_prop->{$arg}{greedy}) {
86 1         28 my $sch = $args_prop->{$arg}{schema};
87             my $is_array_of_simple = $sch && $sch->[0] eq 'array' &&
88 1   33     21 is_simple($sch->[1]{of} // $sch->[1]{each_elem});
89 1         33 for my $el (@{ $iargs{$arg} }) {
  1         4  
90 2 50       7 $argv[$pos] = $is_array_of_simple ? $el : _encode($el);
91 2         4 $pos++;
92             }
93             } else {
94 2         7 $argv[$pos] = _encode($iargs{$arg});
95             }
96 3         22 delete $iargs{$arg};
97             }
98             }
99              
100 7         35 for (sort keys %iargs) {
101 9         20 my $sch = $args_prop->{$_}{schema};
102 9   66     53 my $is_bool = $sch && $sch->[0] eq 'bool';
103             my $is_array_of_simple = $sch && $sch->[0] eq 'array' &&
104 9   33     64 $sch->[1]{of} && is_simple($sch->[1]{of});
105             my $is_hash_of_simple = $sch && $sch->[0] eq 'hash' &&
106 9   66     118 is_simple($sch->[1]{of} // $sch->[1]{each_value} // $sch->[1]{each_elem});
107             my $can_be_comma_separated = $is_array_of_simple &&
108 9   100     63 $sch->[1]{of}[0] =~ /\A(int|float)\z/; # XXX as well as other simple types that cannot contain commas
109 9         13 my $opt = $_; $opt =~ s/_/-/g;
  9         18  
110 9 100       31 my $dashopt = length($opt) > 1 ? "--$opt" : "-$opt";
111 9 100       35 if ($is_bool) {
    100          
    100          
    100          
112 2 100       6 if ($iargs{$_}) {
113 1         5 push @argv, $dashopt;
114             } else {
115 1         6 push @argv, "--no$opt";
116             }
117             } elsif ($can_be_comma_separated) {
118 1         4 push @argv, "$dashopt", join(",", @{ $iargs{$_} });
  1         9  
119             } elsif ($is_array_of_simple) {
120 1         3 for (@{ $iargs{$_} }) {
  1         6  
121 2         9 push @argv, "$dashopt", $_;
122             }
123             } elsif ($is_hash_of_simple) {
124 1         3 my $arg = $iargs{$_};
125 1         8 for (sort keys %$arg) {
126 2         14 push @argv, "$dashopt", "$_=$arg->{$_}";
127             }
128             } else {
129 4 100       11 if (ref $iargs{$_}) {
130 1         7 push @argv, "$dashopt-json", _encode($iargs{$_});
131             } else {
132 3         15 push @argv, $dashopt, "$iargs{$_}";
133             }
134             }
135             }
136 7         61 [200, "OK", \@argv];
137             }
138              
139             1;
140             # ABSTRACT: Convert hash arguments to command-line options (and arguments)
141              
142             __END__