File Coverage

blib/lib/Perinci/Sub/Util/Args.pm
Criterion Covered Total %
statement 69 79 87.3
branch 27 38 71.0
condition 12 12 100.0
subroutine 10 10 100.0
pod 5 5 100.0
total 123 144 85.4


line stmt bran cond sub pod time code
1             package Perinci::Sub::Util::Args;
2              
3 2     2   450933 use 5.010001;
  2         7  
4 2     2   8 use strict 'subs', 'vars';
  2         2  
  2         59  
5 2     2   11 use warnings;
  2         23  
  2         107  
6              
7 2     2   7 use Exporter qw(import);
  2         17  
  2         1750  
8              
9             our $AUTHORITY = 'cpan:PERLANCAR'; # AUTHORITY
10             our $DATE = '2023-10-28'; # DATE
11             our $DIST = 'Perinci-Sub-Util'; # DIST
12             our $VERSION = '0.472'; # VERSION
13              
14             our @EXPORT_OK = qw(
15             args_by_tag
16             argnames_by_tag
17             func_args_by_tag
18             func_argnames_by_tag
19             call_with_its_args
20             );
21              
22             sub args_by_tag {
23 6     6 1 391627 my ($meta, $args, $tag) = @_;
24              
25 6         12 my @res;
26 6 50       25 my $args_prop = $meta->{args} or return ();
27 6         21 my $neg = $tag =~ s/\A!//;
28 6         27 for my $argname (keys %$args_prop) {
29 24         45 my $argspec = $args_prop->{$argname};
30 24 100       49 if ($neg) {
31             next unless !$argspec->{tags} ||
32 4 100 100     11 !(grep {$_ eq $tag} @{$argspec->{tags}});
  5         19  
  3         9  
33             } else {
34             next unless $argspec->{tags} &&
35 20 100 100     55 grep {$_ eq $tag} @{$argspec->{tags}};
  25         121  
  15         36  
36             }
37             push @res, $argname, $args->{$argname}
38 8 100       29 if exists $args->{$argname};
39             }
40 6         55 @res;
41             }
42              
43             sub argnames_by_tag {
44 5     5 1 4850 my ($meta, $tag) = @_;
45              
46 5         10 my @res;
47 5 50       21 my $args_prop = $meta->{args} or return ();
48 5 100       8 my $neg; $neg = 1 if $tag =~ s/\A!//;
  5         21  
49 5         20 for my $argname (keys %$args_prop) {
50 20         34 my $argspec = $args_prop->{$argname};
51 20 100       42 if ($neg) {
52             next unless !$argspec->{tags} ||
53 4 100 100     14 !(grep {$_ eq $tag} @{$argspec->{tags}});
  5         20  
  3         7  
54             } else {
55             next unless $argspec->{tags} &&
56 16 100 100     59 grep {$_ eq $tag} @{$argspec->{tags}};
  20         71  
  12         29  
57             }
58 8         18 push @res, $argname;
59             }
60 5         63 sort @res;
61             }
62              
63             sub _find_meta {
64 2     2   5 my $caller = shift;
65 2         4 my $func_name = shift;
66              
67 2 50       20 if ($func_name =~ /(.+)::(.+)/) {
68 2         3 return ${"$1::SPEC"}{$2};
  2         21  
69             } else {
70 0         0 return ${"$caller->[0]::SPEC"}{$func_name};
  0         0  
71             }
72             }
73              
74             sub func_args_by_tag {
75 1     1 1 4416 my ($func_name, $args, $tag) = @_;
76 1 50       9 my $meta = _find_meta([caller(1)], $func_name)
77             or die "Can't find Rinci function metadata for $func_name";
78 1         6 args_by_tag($meta, $args, $tag);
79             }
80              
81             sub func_argnames_by_tag {
82 1     1 1 4202 my ($func_name, $tag) = @_;
83 1 50       10 my $meta = _find_meta([caller(1)], $func_name)
84             or die "Can't find Rinci function metadata for $func_name";
85 1         5 argnames_by_tag($meta, $tag);
86             }
87              
88             sub call_with_its_args {
89 1     1 1 7423 my ($func_name, $args) = @_;
90              
91 1         3 my ($meta, $func);
92 1 50       12 if ($func_name =~ /(.+)::(.+)/) {
93 1 50       2 defined &{$func_name}
  1         7  
94             or die "Function $func_name not defined";
95 1         2 $func = \&{$func_name};
  1         3  
96 1         3 $meta = ${"$1::SPEC"}{$2};
  1         8  
97             } else {
98 0         0 my @caller = caller(1);
99 0         0 my $fullname = "$caller[0]::$func_name";
100 0 0       0 defined &{$fullname}
  0         0  
101             or die "Function $fullname not defined";
102 0         0 $func = \&{$fullname};
  0         0  
103 0         0 $meta = ${"$caller[0]::SPEC"}{$func_name};
  0         0  
104             }
105 1 50       5 $meta or die "Can't find Rinci function metadata for $func_name";
106              
107 1         2 my @args;
108 1 50       5 if ($meta->{args}) {
109 1         2 for my $argname (keys %{ $meta->{args} }) {
  1         7  
110             push @args, $argname, $args->{$argname}
111 4 100       14 if exists $args->{$argname};
112             }
113             }
114 1         5 $func->(@args);
115             }
116              
117             1;
118             # ABSTRACT: Utility routines related to Rinci arguments
119              
120             __END__
121              
122             =pod
123              
124             =encoding UTF-8
125              
126             =head1 NAME
127              
128             Perinci::Sub::Util::Args - Utility routines related to Rinci arguments
129              
130             =head1 VERSION
131              
132             This document describes version 0.472 of Perinci::Sub::Util::Args (from Perl distribution Perinci-Sub-Util), released on 2023-10-28.
133              
134             =head1 SYNOPSIS
135              
136             package MyPackage;
137              
138             use Perinci::Sub::Util::Args qw(
139             args_by_tag
140             argnames_by_tag
141             func_args_by_tag
142             func_argnames_by_tag
143             call_with_its_args
144             );
145              
146             our %SPEC;
147              
148             my %func1_args;
149              
150             $SPEC{myfunc1} = {
151             v => 1.1,
152             summary => 'My function one',
153             args => {
154             %func1_args = (
155             foo => {tags=>['t1', 't2']},
156             bar => {tags=>['t2', 't3']},
157             baz => {},
158             ),
159             },
160             };
161             sub myfunc1 {
162             my %args = @_;
163             }
164              
165             $SPEC{myfunc2} = {
166             v => 1.1,
167             summary => 'My function two',
168             args => {
169             %func1_args,
170             qux => {tags=>['t3']},
171             },
172             };
173             sub myfunc2 {
174             my %args = @_;
175             my $res = call_with_its_args('myfunc1', \%args);
176             }
177              
178             =head1 DESCRIPTION
179              
180             =head1 FUNCTIONS
181              
182             =head2 args_by_tag
183              
184             Usage:
185              
186             my %args = args_by_tag($meta, \%args0, $tag);
187              
188             Will select only keypairs from C<%args0> arguments which have tag C<$tag>.
189             Examples:
190              
191             my %args = args_by_tag($SPEC{myfunc1}, {foo=>1, bar=>2, baz=>3, qux=>4}, 't2'); # (foo=>1, bar=>2)
192              
193             =head2 argnames_by_tag
194              
195             Usage:
196              
197             my @arg_names = argnames_by_tag($meta, $tag);
198              
199             Will select only argument names which have tag C<$tag>.
200              
201             =head2 func_args_by_tag
202              
203             Usage:
204              
205             my %args = func_args_by_tag($func_name, \%args0, $tag);
206              
207             Like L</args_by_tag> except that instead of supplying Rinci function metadata,
208             you supply a function name. Rinci metadata will be searched in C<%SPEC>
209             variable.
210              
211             =head2 func_argnames_by_tag
212              
213             Usage:
214              
215             my @argnames = func_argnames_by_tag($func_name, $tag);
216              
217             Like L</argnames_by_tag> except that instead of supplying Rinci function
218             metadata, you supply a function name. Rinci metadata will be searched in
219             C<%SPEC> variable.
220              
221             =head2 call_with_its_args
222              
223             Usage:
224              
225             my $res = call_with_its_args($func_name, \%args);
226              
227             Call function with arguments taken from C<%args>. Only arguments which the
228             function declares it accepts will be passed.
229              
230             =head1 HOMEPAGE
231              
232             Please visit the project's homepage at L<https://metacpan.org/release/Perinci-Sub-Util>.
233              
234             =head1 SOURCE
235              
236             Source repository is at L<https://github.com/perlancar/perl-Perinci-Sub-Util>.
237              
238             =head1 AUTHOR
239              
240             perlancar <perlancar@cpan.org>
241              
242             =head1 CONTRIBUTING
243              
244              
245             To contribute, you can send patches by email/via RT, or send pull requests on
246             GitHub.
247              
248             Most of the time, you don't need to build the distribution yourself. You can
249             simply modify the code, then test via:
250              
251             % prove -l
252              
253             If you want to build the distribution (e.g. to try to install it locally on your
254             system), you can install L<Dist::Zilla>,
255             L<Dist::Zilla::PluginBundle::Author::PERLANCAR>,
256             L<Pod::Weaver::PluginBundle::Author::PERLANCAR>, and sometimes one or two other
257             Dist::Zilla- and/or Pod::Weaver plugins. Any additional steps required beyond
258             that are considered a bug and can be reported to me.
259              
260             =head1 COPYRIGHT AND LICENSE
261              
262             This software is copyright (c) 2023, 2020, 2017, 2016, 2015, 2014 by perlancar <perlancar@cpan.org>.
263              
264             This is free software; you can redistribute it and/or modify it under
265             the same terms as the Perl 5 programming language system itself.
266              
267             =head1 BUGS
268              
269             Please report any bugs or feature requests on the bugtracker website L<https://rt.cpan.org/Public/Dist/Display.html?Name=Perinci-Sub-Util>
270              
271             When submitting a bug or request, please include a test-file or a
272             patch to an existing test-file that illustrates the bug or desired
273             feature.
274              
275             =cut