File Coverage

blib/lib/Perinci/Sub/Wrapper.pm
Criterion Covered Total %
statement 600 670 89.5
branch 197 284 69.3
condition 95 155 61.2
subroutine 63 76 82.8
pod 1 38 2.6
total 956 1223 78.1


line stmt bran cond sub pod time code
1             package Perinci::Sub::Wrapper;
2              
3             our $DATE = '2019-04-15'; # DATE
4             our $VERSION = '0.850'; # VERSION
5              
6 17     17   227 use 5.010001;
  17         45  
7 17     17   89 use strict;
  17         256  
  17         233  
8 17     15   1527 use warnings;
  15         394  
  15         416  
9 15     15   5352 use experimental 'smartmatch';
  15         37918  
  15         79  
10 15     15   19784 use Log::ger;
  15         728  
  15         67  
11              
12 15     15   8155 use Data::Dmp qw(dmp);
  15         19469  
  15         736  
13 15     15   518 use Function::Fallback::CoreOrPP qw(clone);
  15         119  
  15         481  
14 15     15   5994 use Perinci::Sub::Normalize qw(normalize_function_metadata);
  15         12902  
  15         741  
15 15     15   6048 use Perinci::Sub::Util qw(err);
  15         25630  
  15         738  
16              
17 15     15   112 use Exporter qw(import);
  15         144  
  15         80204  
18             our @EXPORT_OK = qw(wrap_sub);
19              
20             our $Log_Wrapper_Code = $ENV{LOG_PERINCI_WRAPPER_CODE} // 0;
21              
22             our %SPEC;
23              
24             $SPEC{':package'} = {
25             v => 1.1,
26             summary => 'A multi-purpose subroutine wrapping framework',
27             };
28              
29             # "protocol version" (v). whenever there's a significant change in the basic
30             # structure of the wrapper, which potentially cause some/a lot of property
31             # handlers to stop working, we increase this. property handler must always state
32             # which version it follows in its meta. if unspecified, it's assumed to be 1.
33             our $protocol_version = 2;
34              
35             sub new {
36 113     113 0 311 my ($class) = @_;
37 113         612 bless {}, $class;
38             }
39              
40             sub _check_module {
41 183     183   554 my ($self, $mod) = @_;
42              
43 183 50       830 if ($self->{_args}{core}) {
44 1 0       7 if ($mod =~ /\A(experimental|Scalar::Numeric::Util|Scalar::Util::Numeric::PP)\z/) {
    0          
45 1         3 die "BUG: Requested non-core module '$mod' while wrap arg core=1";
46             } elsif ($mod =~ /\A(warnings|List::Util)\z/) {
47             # core modules
48             } else {
49 1         6 die "BUG: Haven't noted whether module '$mod' is core/non-core";
50             }
51             }
52              
53 183 50       537 if ($self->{_args}{pp}) {
54 1 0       1 if ($mod =~ /\A(List::Util|Scalar::Numeric::Util)\z/) {
    0          
55 1         337 die "BUG: Requested XS module '$mod' while wrap arg pp=1";
56             } elsif ($mod =~ /\A(experimental|warnings|Scalar::Util::Numeric::PP)\z/) {
57             # pp modules
58             } else {
59 1         8 die "BUG: Haven't noted whether module '$mod' is pure-perl/XS";
60             }
61             }
62              
63 183 50       439 if ($self->{_args}{core_or_pp}) {
64 1 0       7 if ($mod =~ /\A(Scalar::Numeric::Util)\z/) {
    0          
65 1         91 die "BUG: Requested non-core XS module '$mod' while wrap arg core_or_pp=1";
66             } elsif ($mod =~ /\A(experimental|warnings|List::Util|Scalar::Util::Numeric::PP)\z/) {
67             # core or pp modules
68             } else {
69 1         2 die "BUG: Haven't noted whether module '$mod' is non-core xs or not";
70             }
71             }
72             }
73              
74             sub _add_module {
75 231     231   819 my ($self, $mod) = @_;
76 231 100       774 unless ($mod ~~ $self->{_modules}) {
77 183         414 local $self->{_cur_section};
78 183         449 $self->select_section('before_sub_require_modules');
79 183 100       1222 if ($mod =~ /\A(use|no) (\S+)/) {
    50          
80 125         438 $self->_check_module($2);
81 125         796 $self->push_lines("$mod;");
82             } elsif ($mod =~ /\A\w+(::\w+)*\z/) {
83 59         202 $self->_check_module($mod);
84 59         213 $self->push_lines("require $mod;");
85             } else {
86 1         9 die "BUG: Invalid module name/statement: $mod";
87             }
88 183         514 push @{ $self->{_modules} }, $mod;
  183         511  
89             }
90             }
91              
92             sub _add_var {
93 101     101   888 my ($self, $var, $value) = @_;
94 101 50       386 unless (exists $self->{_vars}{$var}) {
95 101         240 local $self->{_cur_section};
96 101         314 $self->select_section('declare_vars');
97 101         550 $self->push_lines("my \$$var = ".dmp($value).";");
98 101         338 $self->{_vars}{$var} = $value;
99             }
100             }
101              
102             sub _known_sections {
103              
104             # order=>N regulates the order of code. embed=>1 means the code is for embed
105             # mode only and should not be included in dynamic wrapper code.
106              
107 2455     2455   4530 state $val = {
108             before_sub_require_modules => {order=>1},
109              
110             # reserved by wrapper for setting Perl package and declaring 'sub {'
111             OPEN_SUB => {order=>4},
112              
113             # reserved to say 'my %args = @_;' or 'my @args = @_;' etc
114             ACCEPT_ARGS => {order=>5},
115              
116             # reserved to get args values if converted from array/arrayref
117             ACCEPT_ARGS2 => {order=>6},
118              
119             declare_vars => {order=>7},
120              
121             # for handlers to put stuffs right before eval. for example, 'timeout'
122             # uses this to set ALRM signal handler.
123             before_eval => {order=>10},
124              
125             # reserved by wrapper for generating 'eval {'
126             OPEN_EVAL => {order=>20},
127              
128             # used e.g. to load modules used by validation
129             before_call_before_arg_validation => {order=>31},
130              
131             before_call_arg_validation => {order=>32},
132              
133             # used e.g. by dependency checking
134             before_call_after_arg_validation => {order=>33},
135              
136             # feed arguments to sub
137             before_call_feed_args => {order=>48},
138              
139             # for handlers that *must* do stuffs right before call
140             before_call_right_before_call => {order=>49},
141              
142             # reserved by the wrapper for calling the sub
143             CALL => {order=>50},
144              
145             # for handlers that *must* do stuffs right after call
146             after_call_right_after_call => {order=>51},
147              
148             # reserved by the wrapper for adding/stripping result envelope, this
149             # happens before result validation
150             AFTER_CALL_ADD_OR_STRIP_RESULT_ENVELOPE => {order=>52},
151              
152             # used e.g. to load modules used by validation
153             after_call_before_res_validation => {order=>61},
154              
155             after_call_res_validation => {order=>62},
156              
157             after_call_after_res_validation => {order=>63},
158              
159             # reserved by wrapper to put eval end '}' and capturing result in
160             # $_w_res and $@ in $eval_err
161             CLOSE_EVAL => {order=>70},
162              
163             # for handlers to put checks against $eval_err
164             after_eval => {order=>80},
165              
166             # reserved for returning final result '$_w_res'
167             BEFORE_CLOSE_SUB => {order=>99},
168              
169             # reserved for sub closing '}' line
170             CLOSE_SUB => {order=>100},
171             };
172 2455         4232 $val;
173             }
174              
175             sub section_empty {
176 4821     4821 0 6214 my ($self, $section) = @_;
177 4821         10330 !$self->{_codes}{$section};
178             }
179              
180             sub _needs_eval {
181 111     111   237 my ($self) = @_;
182 111   33     317 !($self->section_empty('before_eval') &&
183             $self->section_empty('after_eval'));
184             }
185              
186             # whether we need to store call result to a variable ($_w_res)
187             sub _needs_store_res {
188 105     105   247 my ($self) = @_;
189 105 100       486 return 1 if $self->{_args}{validate_result};
190 7 50       19 return 1 if $self->_needs_eval;
191 7         516 my $ks = $self->_known_sections;
192 6         29 for (grep {/^after_call/} keys %$ks) {
  132         194  
193 24 50       38 return 1 unless $self->section_empty($_);
194             }
195 6         16 0;
196             }
197              
198             sub _check_known_section {
199 1338     1339   1892 my ($self, $section) = @_;
200 1338         2516 my $ks = $self->_known_sections;
201 1338 50       3226 $ks->{$section} or die "BUG: Unknown code section '$section'";
202             }
203              
204             sub _err {
205 366     367   636 my ($self, $c_status, $c_msg) = @_;
206 366 100       763 if ($self->{_meta}{result_naked}) {
207 8         37 $self->push_lines(
208             "warn 'ERROR ' . ($c_status) . ': '. ($c_msg);",
209             'return undef;',
210             );
211             } else {
212 358         1172 $self->push_lines("return [$c_status, $c_msg];");
213             }
214             }
215              
216             sub _errif {
217 270     271   7025 my ($self, $c_status, $c_msg, $c_cond) = @_;
218 270         888 $self->push_lines("if ($c_cond) {");
219 270         662 $self->indent;
220 270         713 $self->_err($c_status, $c_msg);
221 270         684 $self->unindent;
222 270         477 $self->push_lines('}');
223             }
224              
225             sub select_section {
226 1338     1339 0 2422 my ($self, $section) = @_;
227 1338         2611 $self->_check_known_section($section);
228 1338         1871 $self->{_cur_section} = $section;
229 1338         1628 $self;
230             }
231              
232             sub indent {
233 646     647 0 1079 my ($self) = @_;
234 646         1016 my $section = $self->{_cur_section};
235 646   50     1256 $self->{_codes}{$section} //= undef;
236 646         855 $self->{_levels}{$section}++;
237 646         818 $self;
238             }
239              
240             sub unindent {
241 640     641 0 1044 my ($self) = @_;
242 640         901 my $section = $self->{_cur_section};
243 640   100     1578 $self->{_codes}{$section} //= undef;
244 640         912 $self->{_levels}{$section}--;
245 640         834 $self;
246             }
247              
248             sub get_indent_level {
249 86     86 0 181 my ($self) = @_;
250 86         162 my $section = $self->{_cur_section};
251 86   50     412 $self->{_levels}{$section} // 0;
252             }
253              
254             # line can be code or comment. code should not contain string literals that
255             # cross lines (i.e. contain literal newlines) because push_lines() might add
256             # comment at the end of each line.
257              
258             sub push_lines {
259 2606     2606 0 7121 my ($self, @lines) = @_;
260 2606         3690 my $section = $self->{_cur_section};
261              
262 2606 100       4587 unless (exists $self->{_codes}{$section}) {
263 798         1811 unshift @lines, "# * section: $section";
264             # don't give blank line for the top-most section (order=>0)
265 798 50       1295 unshift @lines, "" if $self->_known_sections->{$section}{order};
266 798         1695 $self->{_codes}{$section} = [];
267 798         1732 $self->{_levels}{$section} = 0;
268             }
269              
270 2606         3495 @lines = map {[$self->{_levels}{$section}, $_]} @lines;
  4736         9899  
271 2606 50       4784 if ($self->{_args}{debug}) {
272 0         0 for my $l (@lines) {
273             $l->[2] =
274             $self->{_cur_handler} ?
275             "$self->{_cur_handler} prio=".$self->{_cur_handler_meta}{prio}
276 0 0       0 : "";
277             }
278             }
279 2606         2858 push @{$self->{_codes}{$section}}, @lines;
  2606         4954  
280 2606         5420 $self;
281             }
282              
283             sub _join_codes {
284 208     208   591 my ($self, $crit, $prev_section_level) = @_;
285 208         270 my @lines;
286 208         364 my $ks = $self->_known_sections;
287 208   100     719 $prev_section_level //= 0;
288 208         274 my $i = 0;
289 208         1108 for my $s (sort {$ks->{$a}{order} <=> $ks->{$b}{order}}
  14900         18122  
290             keys %$ks) {
291 4576 100       6359 next if $self->section_empty($s);
292 1772 100       2780 next unless $crit->(section => $s);
293 678         981 $i++;
294 678         788 for my $l (@{ $self->{_codes}{$s} }) {
  678         1174  
295 4015         4693 $l->[0] += $prev_section_level;
296 4015 50       5646 die "BUG: Negative indent level in line $i (section $s): '$l->[1]'"
297             if $l->[0] < 0;
298 4015         7210 my $s = ($self->{_args}{indent} x $l->[0]) . $l->[1];
299 4015 50       5763 if (defined $l->[2]) {
300 0         0 my $num_ws = 80 - length($s);
301 0 0       0 $num_ws = 1 if $num_ws < 1;
302 0         0 $s .= (" " x $num_ws) . "## $l->[2]";
303             }
304 4015         6049 push @lines, $s;
305             }
306 678         1055 $prev_section_level += $self->{_levels}{$s};
307             }
308 208         1834 [join("\n", @lines), $prev_section_level];
309             }
310              
311             sub _format_dyn_wrapper_code {
312 52     52   114 my ($self) = @_;
313 52         129 my $ks = $self->_known_sections;
314             $self->_join_codes(
315             sub {
316 443     443   770 my %args = @_;
317 443         572 my $section = $args{section};
318 443         1391 !$ks->{$section}{embed};
319 52         369 })->[0];
320             }
321              
322             # for embedded, we need to produce three sections which will be inserted in
323             # different places, demonstrated below:
324             #
325             # $SPEC{foo} = {
326             # ...
327             # };
328             # sub foo {
329             # my %args = @_;
330             # # do stuffs
331             # }
332             #
333             # becomes:
334             #
335             # #PRESUB1: require modules (inserted before sub declaration)
336             # require Data::Dumper;
337             # require Scalar::Util;
338             #
339             # $SPEC{foo} = {
340             # ...
341             # };
342             # #PRESUB2: modify metadata piece-by-piece (inserted before sub declaration &
343             # #after $SPEC{foo}). we're avoiding dumping the new modified metadata because
344             # #metadata might contain coderefs which is sometimes problematic when dumping
345             # {
346             # my $meta = $SPEC{foo};
347             # $meta->{v} = 1.1;
348             # $meta->{result_naked} = 0;
349             # }
350             #
351             # sub foo {
352             # my %args = @_;
353             # #PREAMBLE: before call sections (inserted after accept args), e.g.
354             # #validate arguments, convert argument type, setup eval block
355             # #...
356             #
357             # # do stuffs
358             #
359             # #POSTAMBLE: after call sections (inserted before sub end), e.g.
360             # #validate result, close eval block and do retry/etc.
361             # #...
362             # }
363             sub _format_embed_wrapper_code {
364 52     52   125 my ($self) = @_;
365              
366 52         115 my $res = {};
367 52         123 my $ks = $self->_known_sections;
368 52         85 my $j;
369              
370             $j = $self->_join_codes(
371             sub {
372 443     443   756 my %args = @_;
373 443         548 my $section = $args{section};
374 443         1126 $section =~ /\A(before_sub_require_modules)\z/;
375 52         357 });
376 52         273 $res->{presub1} = $j->[0];
377              
378             # no longer needed/produce, code to modify metadata
379 52         144 $res->{presub2} = '';
380              
381             $j = $self->_join_codes(
382             sub {
383 443     443   786 my %args = @_;
384 443         562 my $section = $args{section};
385 443         561 my $order = $ks->{$section}{order};
386             return 1 if $order > $ks->{ACCEPT_ARGS}{order} &&
387 443 100 100     1292 $order < $ks->{CALL}{order};
388 349         689 0;
389 52         283 }, 1);
390 52         239 $res->{preamble} = $j->[0];
391              
392             $j = $self->_join_codes(
393             sub {
394 443     443   700 my %args = @_;
395 443         548 my $section = $args{section};
396 443         559 my $order = $ks->{$section}{order};
397             return 1 if $order > $ks->{CALL}{order} &&
398 443 100 100     1128 $order < $ks->{CLOSE_SUB}{order};
399 337         665 0;
400 52         285 }, $j->[1]);
401 52         215 $res->{postamble} = $j->[0];
402              
403 52         167 $res;
404             }
405              
406 108     108 0 240 sub handlemeta_v { {} }
407 0     0 0 0 sub handlemeta_name { {} }
408 0     0 0 0 sub handlemeta_summary { {} }
409 0     0 0 0 sub handlemeta_description { {} }
410 0     0 0 0 sub handlemeta_tags { {} }
411 0     0 0 0 sub handlemeta_default_lang { {} }
412 0     0 0 0 sub handlemeta_links { {} }
413 0     0 0 0 sub handlemeta_text_markup { {} }
414 0     0 0 0 sub handlemeta_is_func { {} }
415 0     0 0 0 sub handlemeta_is_meth { {} }
416 0     0 0 0 sub handlemeta_is_class_meth { {} }
417 0     0 0 0 sub handlemeta_examples { {} }
418              
419             # after args
420 4     4 0 15 sub handlemeta_features { {v=>2, prio=>15} }
421             sub handle_features {
422 4     4 0 13 my ($self, %args) = @_;
423              
424 4         7 my $meta = $self->{_meta};
425 4   50     10 my $v = $meta->{features} // {};
426              
427 4         11 $self->select_section('before_call_before_arg_validation');
428              
429 4 50 33     17 if ($v->{tx} && $v->{tx}{req}) {
430 4         13 $self->push_lines('', '# check required transaction');
431 4         14 $self->_errif(412, '"Must run with transaction (pass -tx_manager)"',
432             '!$args{-tx_manager}');
433             }
434             }
435              
436             # run before args
437 108     108 0 430 sub handlemeta_args_as { {v=>2, prio=>1, convert=>1} }
438             sub handle_args_as {
439 108     108 0 479 my ($self, %args) = @_;
440              
441 108         259 my $value = $args{value};
442 108         222 my $new = $args{new};
443 108         223 my $meta = $self->{_meta};
444 108   100     364 my $args_p = $meta->{args} // {};
445 108         259 my $opt_va = $self->{_args}{validate_args};
446              
447             # We support conversion of arguments between hash/hashref/array/arrayref. To
448             # make it simple, currently the algorithm is as follow: we first form the
449             # %args hash. If args_as is already 'hash', we just do 'my %args = @_'.
450             # Otherwise, we convert from the other forms.
451             #
452             # We then validate each argument in %args (code generated in 'args'
453             # handler).
454             #
455             # Finally, unless original args_as is 'hash' we convert to the final form
456             # that the wrapped sub expects.
457             #
458             # This setup is optimal when both the sub and generated wrapper accept
459             # 'hash', but suboptimal for other cases (especially positional ones, as
460             # they have to undergo a round-trip to hash even when both accept 'array').
461             # This will be rectified in the future.
462              
463 108   66     409 my $v = $new // $value;
464              
465 108         310 $self->select_section('ACCEPT_ARGS');
466 108 100       338 if ($v eq 'hash') {
    100          
    50          
467 100 100       380 $self->push_lines(q{die 'BUG: Odd number of hash elements supplied' if @_ % 2;})
468             if $opt_va;
469 100         214 $self->push_lines('my %args = @_;');
470             } elsif ($v eq 'hashref') {
471 2 50       9 $self->push_lines(q{die 'BUG: $_[0] needs to be hashref' if @_ && ref($_[0]) ne "HASH";})
472             if $opt_va;
473 2         6 $self->push_lines('my %args = %{$_[0] // {}};');
474             } elsif ($v =~ /\Aarray(ref)?\z/) {
475 6 100       34 my $ref = $1 ? 1:0;
476 6 100       19 if ($ref) {
477 2 50       8 $self->push_lines(q{die 'BUG: $_[0] needs to be arrayref' if @_ && ref($_[0]) ne "ARRAY";})
478             if $opt_va;
479             }
480 6         18 $self->push_lines('my %args;');
481 6         19 $self->select_section('ACCEPT_ARGS2');
482 6         29 for my $a (sort keys %$args_p) {
483 12         19 my $as = $args_p->{$a};
484 12         43 my $line = '$args{'.dmp($a).'} = ';
485 12 50       458 defined($as->{pos}) or die "Error in args property for arg '$a': ".
486             "no pos defined";
487 12         31 my $pos = int($as->{pos} + 0);
488 12 50       30 $pos >= 0 or die "Error in args property for arg '$a': ".
489             "negative value in pos";
490 12 50 33     53 if ($as->{slurpy} // $as->{greedy}) {
491 0 0       0 if ($ref) {
492 0         0 $line .= '[splice @{$_[0]}, '.$pos.'] if @{$_[0]} > '.$pos;
493             } else {
494 0         0 $line .= '[splice @_, '.$pos.'] if @_ > '.$pos;
495             }
496             } else {
497 12 100       21 if ($ref) {
498 4         12 $line .= '$_[0]['.$pos.'] if @{$_[0]} > '.$pos;
499             } else {
500 8         20 $line .= '$_['.$pos.'] if @_ > '.$pos;
501             }
502             }
503 12         36 $self->push_lines("$line;");
504             }
505             } else {
506 0         0 die "Unknown args_as value '$v'";
507             }
508              
509 108         316 $self->select_section('ACCEPT_ARGS');
510 108 100       531 if ($value eq 'hashref') {
    100          
    100          
511 2         4 $self->push_lines('my $args;');
512             } elsif ($value eq 'array') {
513 4         12 $self->push_lines('my @args;');
514             } elsif ($value eq 'arrayref') {
515 2         5 $self->push_lines('my $args;');
516             }
517              
518 108         152 my $tok;
519 108         277 $self->select_section('before_call_feed_args');
520 108         170 $v = $value;
521 108 100       296 if ($v eq 'hash') {
    100          
    50          
522 100         182 $tok = '%args';
523             } elsif ($v eq 'hashref') {
524 2         5 $tok = '$args';
525 2         6 $self->push_lines($tok.' = \%args;'); # XXX should we set each arg instead?
526             } elsif ($v =~ /\Aarray(ref)?\z/) {
527 6 100       19 my $ref = $1 ? 1:0;
528 6 100       18 $tok = ($ref ? '$':'@') . 'args';
529 6         43 for my $a (sort {$args_p->{$a}{pos} <=> $args_p->{$b}{pos}}
  6         25  
530             keys %$args_p) {
531 12         22 my $as = $args_p->{$a};
532 12         35 my $t = '$args{'.dmp($a).'}';
533 12         335 my $line;
534 12 50       29 defined($as->{pos}) or die "Error in args property for arg '$a': ".
535             "no pos defined";
536 12         23 my $pos = int($as->{pos} + 0);
537 12 50       30 $pos >= 0 or die "Error in args property for arg '$a': ".
538             "negative value in pos";
539 12 50 33     56 if ($as->{slurpy} // $as->{greedy}) {
540 0         0 $line = 'splice @args, '.$pos.', scalar(@args)-1, @{'.$t.'}';
541             } else {
542 12 100       42 $line = '$args'.($ref ? '->':'').'['.$pos."] = $t if exists $t";
543             }
544 12         33 $self->push_lines("$line;");
545             }
546             } else {
547 0         0 die "Unknown args_as value '$v'";
548             }
549 108         550 $self->{_args_token} = $tok;
550             }
551              
552             sub _sah {
553 11     11   4218 require Data::Sah;
554              
555 11         22775 my $self = shift;
556 11         47 state $sah = Data::Sah->new;
557 11         289 $sah;
558             }
559              
560             sub _plc {
561 86     86   147 my $self = shift;
562 86         563 state $plc = do {
563 7         25 my $plc = $self->_sah->get_compiler("perl");
564 7         336100 $plc->comment_style('shell2'); # to make all comment uses ## instead of #
565 7         107 $plc;
566             };
567             }
568              
569             sub _handle_args {
570 78     78   338 my ($self, %args) = @_;
571              
572 78   66     430 my $v = $args{v} // $self->{_meta}{args};
573 78 50       201 return unless $v;
574              
575 78         193 my $opt_sin = $self->{_args}{_schema_is_normalized};
576 78         157 my $opt_va = $self->{_args}{validate_args};
577              
578 78   100     302 my $prefix = $args{prefix} // '';
579 78   100     304 my $argsterm = $args{argsterm} // '%args';
580              
581 78 100       193 if ($opt_va) {
582 76         288 $self->_add_module("use experimental 'smartmatch'");
583 76         233 $self->select_section('before_call_arg_validation');
584 76 100       331 $self->push_lines('', '# check args') if $prefix eq '';
585 76         351 $self->push_lines("for (sort keys $argsterm) {");
586 76         196 $self->indent;
587 76         361 $self->_errif(400, q["Invalid argument name (please use letters/numbers/underscores only)'].$prefix.q[$_'"],
588             '!/\A(-?)\w+(\.\w+)*\z/o');
589 76         613 $self->_errif(400, q["Unknown argument '].$prefix.q[$_'"],
590             '!($1 || $_ ~~ '.dmp([sort keys %$v]).')');
591 76         236 $self->unindent;
592 76         164 $self->push_lines('}');
593             }
594              
595 78         323 for my $argname (sort keys %$v) {
596 108         228 my $argspec = $v->{$argname};
597              
598 108         186 my $argterm = $argsterm;
599 108 100       739 if ($argterm =~ /^%\{\s*(.+)\s*\}/) {
    50          
600 8         35 $argterm = $1 . "->{'$argname'}";
601             } elsif ($argterm =~ s/^%/\$/) {
602 100         267 $argterm .= "{'$argname'}";
603             } else {
604 0         0 $argterm .= "->{'$argname'}";
605             }
606              
607 108         233 my $has_default_prop = exists($argspec->{default});
608 108         269 my $sch = $argspec->{schema};
609              
610 108 100       287 if ($sch) {
    100          
611             my $has_sch_default = ref($sch) eq 'ARRAY' &&
612 78 100 100     470 exists($sch->[1]{default}) ? 1:0;
613 78 100       182 if ($opt_va) {
614              
615 74         306 $self->push_lines("if (exists($argterm)) {");
616 74         181 $self->indent;
617              
618 74 100       185 if ($argspec->{stream}) {
619 2 50       15 die "Error in schema for argument '$argname': must be str/buf/array if stream=1"
620             unless $sch->[0] =~ /\A(str|buf|array)\z/; # XXX allow 'any' if all of its 'of' values are str/buf/array
621             die "Error in schema for argument '$argname': must specify 'of' array clause if stream=1"
622 2 50 33     13 if $sch->[0] eq 'array' && !$sch->[1]{of};
623              
624 2         15 $self->_errif(
625             400,
626             qq["Argument '$prefix$argname' (stream) fails validation: must be coderef"],
627             "!(ref($argterm) eq 'CODE')",
628             );
629 2         6 $self->push_lines('{ ## introduce scope because we want to declare a generic variable $i');
630 2         5 $self->indent;
631 2         8 $self->push_lines(
632             'my $i = -1;',
633             "my \$origsub = $argterm;",
634             '# arg coderef wrapper for validation',
635             "$argterm = sub {",
636             );
637 2         6 $self->indent;
638 2         6 $self->push_lines(
639             '$i++;',
640             "my \$rec = \$origsub->();",
641             'return undef unless defined $rec;',
642             );
643             }
644              
645 74         128 my $dn = $argname; $dn =~ s/\W+/_/g;
  74         177  
646             my $cd = $self->_plc->compile(
647             data_name => $dn,
648             data_term => $argspec->{stream} ? '$rec' : $argterm,
649             schema => $argspec->{stream} && $sch->[0] eq 'array' ? $sch->[1]{of} : $sch,
650             schema_is_normalized => $opt_sin,
651             return_type => 'str',
652             indent_level => $self->get_indent_level + 1,
653             core => $self->{_args}{core},
654             core_or_pp => $self->{_args}{core_or_pp},
655             pp => $self->{_args}{pp},
656 74 100 66     209 %{ $self->{_args}{_extra_sah_compiler_args} // {}},
  74 100 50     616  
657             );
658 72 50       512738 die "Incompatible Data::Sah version (cd v=$cd->{v}, expected 2)" unless $cd->{v} == 2;
659 72         142 for my $mod_rec (@{ $cd->{modules} }) {
  72         204  
660 208 100       471 next unless $mod_rec->{phase} eq 'runtime';
661 136   66     603 $self->_add_module($mod_rec->{use_statement} // $mod_rec->{name});
662             }
663             $self->_add_var($_, $cd->{vars}{$_})
664 72         134 for sort keys %{ $cd->{vars} };
  72         257  
665 72         353 $cd->{result} =~ s/\A\s+//;
666 72         379 $self->push_lines(
667             "my \$err_$dn;",
668             "$cd->{result};",
669             );
670 72 100       214 if ($argspec->{stream}) {
671 2         15 $self->push_lines(
672             'if ('."\$err_$dn".') { die "Record #$i of streaming argument '."'$prefix$argname'".' ($rec) fails validation: '."\$err_$dn".'" }',
673             '$rec;',
674             );
675             } else {
676 70         364 $self->_errif(
677             400, qq["Argument '$prefix$argname' fails validation: \$err_$dn"],
678             "\$err_$dn");
679             }
680 72 100       199 if ($argspec->{meta}) {
681 2         10 $self->push_lines("# check subargs of $prefix$argname");
682             $self->_handle_args(
683             %args,
684             v => $argspec->{meta}{args},
685 2 50       31 prefix => ($prefix ? "$prefix/" : "") . "$argname/",
686             argsterm => '%{'.$argterm.'}',
687             );
688             }
689 72 100       192 if ($argspec->{element_meta}) {
690 2         10 $self->push_lines("# check element subargs of $prefix$argname");
691 2         5 my $indexterm = "$prefix$argname";
692 2         6 $indexterm =~ s/\W+/_/g;
693 2         5 $indexterm = '$i_' . $indexterm;
694 2         10 $self->push_lines('for my '.$indexterm.' (0..$#{ '.$argterm.' }) {');
695 2         6 $self->indent;
696 2         13 $self->_errif(
697             400, qq("Argument '$prefix$argname\[).qq($indexterm]' fails validation: must be hash"),
698             "ref($argterm\->[$indexterm]) ne 'HASH'");
699             $self->_handle_args(
700             %args,
701             v => $argspec->{element_meta}{args},
702 2 50       24 prefix => ($prefix ? "$prefix/" : "") . "$argname\[$indexterm]/",
703             argsterm => '%{'.$argterm.'->['.$indexterm.']}',
704             );
705 2         7 $self->unindent;
706 2         5 $self->push_lines('}');
707             }
708 72         191 $self->unindent;
709 72 100       193 if ($argspec->{stream}) {
710 2         7 $self->push_lines('}; ## arg coderef wrapper');
711 2         4 $self->unindent;
712 2         5 $self->push_lines('} ## close scope');
713 2         5 $self->unindent;
714             }
715 72 100       238 if ($has_default_prop) {
    100          
716             $self->push_lines(
717             '} else {',
718 8         58 " $argterm //= ".dmp($argspec->{default}).";");
719             } elsif ($has_sch_default) {
720             $self->push_lines(
721             '} else {',
722 8         42 " $argterm //= ".dmp($sch->[1]{default}).";");
723             }
724 72         234 $self->push_lines("} ## if exists arg $prefix$argname");
725             } # if opt_va
726              
727             } elsif ($has_default_prop) {
728             # doesn't have schema but have 'default' property, we still need to
729             # set default here
730 2         9 $self->push_lines("$argterm = ".dmp($argspec->{default}).
731             " if !exists($argterm);");
732             }
733 106 100 100     1114 if ($argspec->{req} && $opt_va) {
734 18         94 $self->_errif(
735             400, qq["Missing required argument: $argname"],
736             "!exists($argterm)");
737             }
738             } # for arg
739             }
740              
741 74     74 0 329 sub handlemeta_args { {v=>2, prio=>10} }
742             sub handle_args {
743 74     74 0 320 my ($self, %args) = @_;
744 74         350 $self->_handle_args(%args);
745             }
746              
747             # after args
748 4     4 0 16 sub handlemeta_args_rels { {v=>2, prio=>11} }
749             sub handle_args_rels {
750 4     4 0 15 my ($self, %args) = @_;
751              
752 4   33     20 my $v = $args{v} // $self->{_meta}{args_rels};
753 4 50       10 return unless $v;
754              
755 4   50     15 my $argsterm = $args{argsterm} // '%args';
756              
757 4         10 $self->select_section('before_call_arg_validation');
758 4         9 $self->push_lines('', '# check args_rels');
759              
760 4         7 my $dn = "args_rels";
761 4         11 my $hc = $self->_sah->get_compiler("human");
762 4         17501 my $cd_h = $hc->init_cd;
763 4   33     1738 $cd_h->{args}{lang} //= $cd_h->{default_lang};
764              
765             my $cd = $self->_plc->compile(
766             data_name => $dn,
767             data_term => "\\$argsterm",
768             schema => ['hash', $v],
769             return_type => 'str',
770             indent_level => $self->get_indent_level + 1,
771             human_hash_values => {
772             field => $hc->_xlt($cd_h, "argument"),
773             fields => $hc->_xlt($cd_h, "arguments"),
774             },
775             core => $self->{_args}{core},
776             core_or_pp => $self->{_args}{core_or_pp},
777             pp => $self->{_args}{pp},
778 4         15 );
779 2 50       18022 die "Incompatible Data::Sah version (cd v=$cd->{v}, expected 2)" unless $cd->{v} == 2;
780 2         5 for my $mod_rec (@{ $cd->{modules} }) {
  2         8  
781 6 100       16 next unless $mod_rec->{phase} eq 'runtime';
782 4   66     19 $self->_add_module($mod_rec->{use_statement} // $mod_rec->{name});
783             }
784 2         5 $self->_add_var($_, $cd->{vars}{$_}) for sort keys %{ $cd->{vars} };
  2         8  
785 2         11 $cd->{result} =~ s/\A\s+//;
786 2         13 $self->push_lines(
787             "my \$err_$dn;",
788             "$cd->{result};",
789             );
790 2         10 $self->_errif(
791             400, qq["\$err_$dn"],
792             "\$err_$dn");
793             }
794              
795 14     14 0 41 sub handlemeta_result { {v=>2, prio=>50} }
796             sub handle_result {
797 14     14 0 608 require Data::Sah;
798              
799 14         3050 my ($self, %args) = @_;
800              
801 14         29 my $meta = $self->{_meta};
802 14         30 my $v = $meta->{result};
803 14 50       34 return unless $v;
804              
805 14         29 my $opt_sin = $self->{_args}{_schema_is_normalized};
806 14         24 my $opt_vr = $self->{_args}{validate_result};
807              
808 14         30 my %schemas_by_status; # key = status, value = schema
809              
810             # collect and check handlers
811             my %handler_args;
812 14         0 my %handler_metas;
813 14         46 for my $k0 (keys %$v) {
814 18         28 my $k = $k0;
815 18         36 $k =~ s/\..+//;
816 18 100       44 next if $k =~ /\A_/;
817              
818             # check builtin result spec key
819 16 50       95 next if $k =~ /\A(
820             summary|description|tags|default_lang|
821             schema|statuses|stream|
822             x
823             )\z/x;
824             # try a property module first
825 0         0 require "Perinci/Sub/Property/result/$k.pm";
826 0         0 my $meth = "handlemeta_result__$k";
827 0 0       0 unless ($self->can($meth)) {
828 0         0 die "No handler for property result/$k0 ($meth)";
829             }
830 0         0 my $hm = $self->$meth;
831 0   0     0 $hm->{v} //= 1;
832 0 0       0 next unless defined $hm->{prio};
833             die "Please update property handler result/$k which is still at v=$hm->{v} ".
834             "(needs v=$protocol_version)"
835 0 0       0 unless $hm->{v} == $protocol_version;
836             my $ha = {
837 0         0 prio=>$hm->{prio}, value=>$v->{$k0}, property=>$k0,
838             meth=>"handle_result__$k",
839             };
840 0         0 $handler_args{$k} = $ha;
841 0         0 $handler_metas{$k} = $hm;
842             }
843              
844             # call all the handlers in order
845 14         40 for my $k (sort {$handler_args{$a}{prio} <=> $handler_args{$b}{prio}}
  0         0  
846             keys %handler_args) {
847 0         0 my $ha = $handler_args{$k};
848 0         0 my $meth = $ha->{meth};
849 0         0 local $self->{_cur_handler} = $meth;
850 0         0 local $self->{_cur_handler_meta} = $handler_metas{$k};
851 0         0 local $self->{_cur_handler_args} = $ha;
852 0         0 $self->$meth(args=>\%args, meta=>$meta, %$ha);
853             }
854              
855             # validate result
856 14         19 my @modules;
857 14 100 100     60 if ($v->{schema} && $opt_vr) {
858 4         9 $schemas_by_status{200} = $v->{schema};
859             }
860 14 50 33     37 if ($v->{statuses} && $opt_vr) {
861 0         0 for my $s (keys %{$v->{statuses}}) {
  0         0  
862 0         0 my $sv = $v->{statuses}{$s};
863 0 0       0 if ($sv->{schema}) {
864 0         0 $schemas_by_status{$s} = $sv->{schema};
865             }
866             }
867             }
868              
869 14         29 my $sub_name = $self->{_args}{sub_name};
870              
871 14 100       40 if ($opt_vr) {
872 12         33 $self->select_section('after_call_res_validation');
873             $self->push_lines(
874             'my $_w_res2 = $_w_res->[2];',
875 12 100       53 'my $_w_res_is_stream = $_w_res->[3]{stream} // ' . ($v->{stream} ? 1:0) . ';',
876             );
877 12         32 $self->_errif(
878             500,
879             q["Stream result must be coderef"],
880             '$_w_res_is_stream && ref($_w_res2) ne "CODE"',
881             );
882 12         51 for my $s (sort keys %schemas_by_status) {
883 4         7 my $sch = $schemas_by_status{$s};
884 4 100       25 if ($v->{stream}) {
885 2 50       11 die "Error in result schema: must be str/buf/array if stream=1"
886             unless $sch->[0] =~ /\A(str|buf|array)\z/; # XXX allow 'any' if all of its 'of' values are str/buf/array
887             die "Error in result schema: must specify 'of' array clause if stream=1"
888 2 50 33     13 if $sch->[0] eq 'array' && !$sch->[1]{of};
889             }
890 4         16 $self->push_lines("if (\$_w_res->[0] == $s) {");
891 4         8 $self->indent;
892 4         19 $self->push_lines('if (!$_w_res_is_stream) {');
893 4         10 $self->indent;
894              
895             # validation for when not a stream
896             my $cd = $self->_plc->compile(
897             data_name => '_w_res2',
898             # err_res can clash on arg named 'res'
899             err_term => '$_w_err2_res',
900             schema => $sch,
901             schema_is_normalized => $opt_sin,
902             return_type => 'str',
903             indent_level => $self->get_indent_level + 1,
904             core => $self->{_args}{core},
905             core_or_pp => $self->{_args}{core_or_pp},
906             pp => $self->{_args}{pp},
907 4   50     15 %{ $self->{_args}{_extra_sah_compiler_args} // {}},
  4         32  
908             );
909 4 50       66905 die "Incompatible Data::Sah version (cd v=$cd->{v}, expected 2)" unless $cd->{v} == 2;
910 4         10 for my $mod_rec (@{ $cd->{modules} }) {
  4         12  
911 16 100       36 next unless $mod_rec->{phase} eq 'runtime';
912 10   66     42 $self->_add_module($mod_rec->{use_statement} // $mod_rec->{name});
913             }
914             $self->_add_var($_, $cd->{vars}{$_})
915 4         7 for sort keys %{ $cd->{vars} };
  4         17  
916 4         10 $self->push_lines("my \$_w_err2_res;");
917 4         21 $cd->{result} =~ s/\A\s+//;
918 4         15 $self->push_lines("$cd->{result};");
919 4         20 $self->_errif(
920             500,
921             qq["BUG: Result from sub $sub_name (\$_w_res2) fails validation: ].
922             qq[\$_w_err2_res"],
923             "\$_w_err2_res");
924 4         9 $self->unindent;
925 4         8 $self->push_lines("} else {"); # stream
926 4         10 $self->indent;
927 4         12 $self->push_lines(
928             'my $i = -1;',
929             '# wrap result coderef for validation',
930             '$_w_res->[2] = sub {',
931             );
932 4         9 $self->indent;
933 4         10 $self->push_lines(
934             '$i++;',
935             'my $rec = $_w_res2->();',
936             'return undef unless defined $rec;',
937             );
938             # generate schema code once again, this time for when stream
939             $cd = $self->_plc->compile(
940             data_name => 'rec',
941             # err_res can clash on arg named 'res'
942             err_term => '$rec_err',
943             schema => $sch->[0] eq 'array' ? $sch->[1]{of} : $sch,
944             schema_is_normalized => $opt_sin,
945             return_type => 'str',
946             indent_level => $self->get_indent_level + 1,
947             core => $self->{_args}{core},
948             core_or_pp => $self->{_args}{core_or_pp},
949             pp => $self->{_args}{pp},
950 4 100 50     10 %{ $self->{_args}{_extra_sah_compiler_args} // {}},
  4         27  
951             );
952 4 50       8296 die "Incompatible Data::Sah version (cd v=$cd->{v}, expected 2)" unless $cd->{v} == 2;
953             # XXX no need to require modules required by validator?
954 4         12 $self->push_lines('my $rec_err;');
955 4         18 $cd->{result} =~ s/\A\s+//;
956 4         17 $self->push_lines("$cd->{result};");
957 4         9 $self->push_lines('if ($rec_err) { die "BUG: Result stream record #$i ($rec) fails validation: $rec_err" }');
958 4         9 $self->push_lines('$rec;');
959 4         9 $self->unindent;
960 4         10 $self->push_lines('}; ## result coderef wrapper');
961 4         10 $self->unindent;
962 4         7 $self->push_lines("} ## if stream");
963 4         10 $self->unindent;
964 4         10 $self->push_lines("} ## if status=$s");
965             } # for schemas_by_status
966             }
967             }
968              
969 6     6 0 28 sub handlemeta_result_naked { {v=>2, prio=>99, convert=>1} }
970             sub handle_result_naked {
971 6     6 0 34 my ($self, %args) = @_;
972              
973 6         17 my $old = $args{value};
974 6   33     20 my $v = $args{new} // $old;
975              
976 6 50       19 return if !!$v == !!$old;
977              
978 6         19 $self->select_section('AFTER_CALL_ADD_OR_STRIP_RESULT_ENVELOPE');
979 6 100       19 if ($v) {
980 2         7 $self->push_lines(
981             '', '# strip result envelope',
982             '$_w_res = $_w_res->[2];',
983             );
984             } else {
985 4         15 $self->push_lines(
986             '', '# add result envelope',
987             '$_w_res = [200, "OK", $_w_res];',
988             );
989             }
990             }
991              
992 4     4 0 13 sub handlemeta_deps { {v=>2, prio=>0.5} }
993             sub handle_deps {
994 4     4 0 17 my ($self, %args) = @_;
995 4         10 my $value = $args{value};
996 4         7 my $meta = $self->{_meta};
997 4         8 my $v = $self->{_args}{meta_name};
998 4         10 $self->select_section('before_call_after_arg_validation');
999 4         11 $self->push_lines('', '# check dependencies');
1000 4         15 $self->_add_module("Perinci::Sub::DepChecker");
1001             #$self->push_lines('use Data::Dump; dd '.$v.';');
1002 4         16 $self->push_lines('my $_w_deps_res = Perinci::Sub::DepChecker::check_deps('.
1003             $v.'->{deps});');
1004 4         17 $self->_errif(412, '"Deps failed: $_w_deps_res"', '$_w_deps_res');
1005              
1006             # we handle some deps our own
1007 4 50       16 if ($value->{tmp_dir}) {
1008 0         0 $self->_errif(412, '"Dep failed: please specify -tmp_dir"',
1009             '!$args{-tmp_dir}');
1010             }
1011 4 50       12 if ($value->{trash_dir}) {
1012 0         0 $self->_errif(412, '"Dep failed: please specify -trash_dir"',
1013             '!$args{-trash_dir}');
1014             }
1015 4 50       22 if ($value->{undo_trash_dir}) {
1016 0         0 $self->_errif(412, '"Dep failed: please specify -undo_trash_dir"',
1017             '!($args{-undo_trash_dir} || $args{-tx_manager} || '.
1018             '$args{-undo_action} && $args{-undo_action}=~/\A(?:undo|redo)\z/)');
1019             }
1020             }
1021              
1022 110     110 0 223 sub handlemeta_x { {} }
1023 0     0 0 0 sub handlemeta_entity_v { {} }
1024 0     0 0 0 sub handlemeta_entity_date { {} }
1025              
1026             sub _reset_work_data {
1027 108     108   437 my ($self, %args) = @_;
1028              
1029             # to make it stand out more, all work/state data is prefixed with
1030             # underscore.
1031              
1032 108         522 $self->{_cur_section} = undef;
1033 108         271 $self->{_cur_handler} = undef;
1034 108         246 $self->{_cur_handler_args} = undef;
1035 108         201 $self->{_cur_handler_meta} = undef;
1036 108         254 $self->{_levels} = {};
1037 108         250 $self->{_codes} = {};
1038 108         259 $self->{_modules} = []; # modules loaded by wrapper sub
1039 108         578 $self->{$_} = $args{$_} for keys %args;
1040             }
1041              
1042             sub wrap {
1043 112     112 0 834 require Scalar::Util;
1044              
1045 112         384 my ($self, %args) = @_;
1046              
1047 112         259 my $wrap_log_prop = "x.perinci.sub.wrapper.logs";
1048              
1049             # required arguments
1050 112         217 my $sub = $args{sub};
1051 112         273 my $sub_name = $args{sub_name};
1052 112 50 66     405 $sub || $sub_name or return [400, "Please specify sub or sub_name"];
1053 112 50       314 $args{meta} or return [400, "Please specify meta"];
1054 112         219 my $meta_name = $args{meta_name};
1055             # we clone the meta because we'll replace stuffs
1056 112         336 my $meta = clone($args{meta});
1057 112   100     1900 my $wrap_logs = $meta->{$wrap_log_prop} // [];
1058              
1059             # currently internal args, not exposed/documented
1060 112   50     603 $args{_compiled_package} //= 'Perinci::Sub::Wrapped';
1061 112         228 my $comppkg = $args{_compiled_package};
1062             $args{_schema_is_normalized} //=
1063 112 100 66     808 $wrap_logs->[-1] && $wrap_logs->[-1]{normalize_schema} ? 1 : 0;
      100        
1064 112   50     485 $args{_embed} //= 0;
1065 112   50     525 $args{_extra_sah_compiler_args} //= undef;
1066              
1067             # defaults for arguments
1068 112   50     703 $args{indent} //= " " x 4;
1069 112   100     478 $args{convert} //= {};
1070 112   50     544 $args{compile} //= 1;
1071 112   100     527 $args{log} //= 1;
1072             $args{validate_args} //= 0
1073             # function might want to disable validate_args by default, e.g. if
1074             # source code has been processed with
1075             # Dist::Zilla::Plugin::Rinci::Validate
1076 112 100 50     322 if $meta->{'x.perinci.sub.wrapper.disable_validate_args'};
1077             $args{validate_args} //= 0
1078             # by default do not validate args again if previous wrapper(s) have
1079             # already done it
1080 112 100 50     414 if (grep {$_->{validate_args}} @$wrap_logs);
  2         14  
1081 112   100     548 $args{validate_args} //= 1;
1082             $args{validate_result} //= 0
1083             # function might want to disable validate_result by default, e.g. if
1084             # source code has been processed with
1085             # Dist::Zilla::Plugin::Rinci::Validate
1086 112 100 50     299 if $meta->{'x.perinci.sub.wrapper.disable_validate_result'};
1087             $args{validate_result} //= 0
1088             # by default do not validate result again if previous wrapper(s) have
1089             # already done it
1090 112 100 50     302 if (grep {$_->{validate_result}} @$wrap_logs);
  2         11  
1091 112   100     580 $args{validate_result} //= 1;
1092 112   33     526 $args{core} //= $ENV{PERINCI_WRAPPER_CORE};
1093 112   33     556 $args{core_or_pp} //= $ENV{PERINCI_WRAPPER_CORE_OR_PP};
1094 112   33     624 $args{pp} //= $ENV{PERINCI_WRAPPER_PP};
1095              
1096 112         177 my $sub_ref_name;
1097             # if sub_name is not provided, create a unique name for it. it is needed by
1098             # the wrapper-generated code (e.g. printing error messages)
1099 112 100 66     392 if (!$sub_name || $sub) {
1100 110         570 my $n = $comppkg . "::sub".Scalar::Util::refaddr($sub);
1101 15     14   501 no strict 'refs'; no warnings; ${$n} = $sub;
  14     14   122  
  14         504  
  14         431  
  14         49  
  14         630  
  110         177  
  110         511  
1102 14     14   88 use experimental 'smartmatch';
  14         134  
  14         158  
1103 110 50       316 if (!$sub_name) {
1104 110         346 $args{sub_name} = $sub_name = '$' . $n;
1105             }
1106 110         279 $sub_ref_name = '$' . $n;
1107             }
1108             # if meta name is not provided, we store the meta somewhere, it is needed by
1109             # the wrapper-generated code (e.g. deps clause).
1110 112 50       480 if (!$meta_name) {
1111 112         406 my $n = $comppkg . "::meta".Scalar::Util::refaddr($meta);
1112 14     14   1521 no strict 'refs'; no warnings; ${$n} = $meta;
  14     14   50  
  14         365  
  14         88  
  14         95  
  14         577  
  112         191  
  112         565  
1113 14     14   362 use experimental 'smartmatch';
  14         32  
  14         60  
1114 112         390 $args{meta_name} = $meta_name = '$' . $n;
1115             }
1116              
1117             # shallow copy
1118 112         200 my $opt_cvt = { %{ $args{convert} } };
  112         345  
1119 112         229 my $opt_sin = $args{_schema_is_normalized};
1120              
1121 112 100       687 $meta = normalize_function_metadata($meta)
1122             unless $opt_sin;
1123              
1124 108         55907 $self->_reset_work_data(_args=>\%args, _meta=>$meta);
1125              
1126             # add properties from convert, if not yet mentioned in meta
1127 108         375 for (keys %$opt_cvt) {
1128 10 100       41 $meta->{$_} = undef unless exists $meta->{$_};
1129             }
1130              
1131             # mark in the metadata that we have done the wrapping, so future wrapping
1132             # can avoid needless duplicated functionality (like validating args twice).
1133             # note that handler can log their mark too.
1134             {
1135 108   100     189 my @wrap_log = @{ $meta->{$wrap_log_prop} // [] };
  108         195  
  108         581  
1136             push @wrap_log, {
1137             validate_args => $args{validate_args},
1138             validate_result => $args{validate_result},
1139 108         576 normalize_schema => !$opt_sin,
1140             };
1141 108 100       345 if ($args{log}) {
1142 106         338 $meta->{$wrap_log_prop} = \@wrap_log;
1143             }
1144             }
1145              
1146             # start iterating over properties
1147              
1148 108         484 $self->select_section('OPEN_SUB');
1149 108         576 $self->push_lines(
1150             "package $comppkg;", 'sub {');
1151 108         359 $self->indent;
1152              
1153 108   100     511 $meta->{args_as} //= "hash";
1154              
1155 108 100       476 if ($meta->{args_as} =~ /hash/) {
1156 102         236 $self->select_section('before_call_after_arg_validation');
1157             # tell function it's being wrapped, currently disabled
1158             #$self->push_lines('$args{-wrapped} = 1;');
1159             }
1160              
1161 108         376 my %props = map {$_=>1} keys %$meta;
  434         845  
1162 108         360 $props{$_} = 1 for keys %$opt_cvt;
1163              
1164             # collect and check handlers
1165 108         220 my %handler_args;
1166             my %handler_metas;
1167 108         278 for my $k0 (keys %props) {
1168 434         537 my $k = $k0;
1169 434         893 $k =~ s/\..+//;
1170 434 100       888 next if $k =~ /\A_/;
1171 432 50       786 next if $handler_args{$k};
1172             #if ($k ~~ $self->{_args}{skip}) {
1173             # $log->tracef("Skipped property %s (mentioned in skip)", $k);
1174             # next;
1175             #}
1176 432 50       1244 return [500, "Invalid property name $k"] unless $k =~ /\A\w+\z/;
1177 432         838 my $meth = "handlemeta_$k";
1178 432 50       1531 unless ($self->can($meth)) {
1179             # try a property module first
1180 0         0 require "Perinci/Sub/Property/$k.pm";
1181 0 0       0 unless ($self->can($meth)) {
1182 0         0 return [500, "No handler for property $k0 ($meth)"];
1183             }
1184             }
1185 432         1301 my $hm = $self->$meth;
1186 432   100     1248 $hm->{v} //= 1;
1187 432 100       1014 next unless defined $hm->{prio};
1188             die "Please update property handler $k which is still at v=$hm->{v} ".
1189             "(needs v=$protocol_version)"
1190 214 50       565 unless $hm->{v} == $protocol_version;
1191             my $ha = {
1192 214         965 prio=>$hm->{prio}, value=>$meta->{$k0}, property=>$k0,
1193             meth=>"handle_$k",
1194             };
1195 214 100       475 if (exists $opt_cvt->{$k0}) {
1196             return [501, "Property '$k0' does not support conversion"]
1197 10 50       35 unless $hm->{convert};
1198 10         27 $ha->{new} = $opt_cvt->{$k0};
1199 10         29 $meta->{$k0} = $opt_cvt->{$k0};
1200             }
1201 214         398 $handler_args{$k} = $ha;
1202 214         448 $handler_metas{$k} = $hm;
1203             }
1204              
1205             # call all the handlers in order
1206 108         583 for my $k (sort {$handler_args{$a}{prio} <=> $handler_args{$b}{prio}}
  117         500  
1207             keys %handler_args) {
1208 214         363 my $ha = $handler_args{$k};
1209 214         352 my $meth = $ha->{meth};
1210 214         454 local $self->{_cur_handler} = $meth;
1211 214         414 local $self->{_cur_handler_meta} = $handler_metas{$k};
1212 214         362 local $self->{_cur_handler_args} = $ha;
1213 214         1130 $self->$meth(args=>\%args, meta=>$meta, %$ha);
1214             }
1215              
1216 104         403 my $needs_store_res = $self->_needs_store_res;
1217 104 100       277 if ($needs_store_res) {
1218 98         328 $self->_add_var('_w_res');
1219             }
1220              
1221 104         293 $self->select_section('CALL');
1222 104   66     299 my $sn = $sub_ref_name // $sub_name;
1223             $self->push_lines(
1224             ($needs_store_res ? '$_w_res = ' : "") .
1225             $sn. ($sn =~ /^\$/ ? "->" : "").
1226 104 100       919 "(".$self->{_args_token}.");");
    100          
1227 104 100       321 if ($args{validate_result}) {
1228 98         288 $self->select_section('after_call_before_res_validation');
1229 98 100       347 unless ($meta->{result_naked}) {
1230 96         441 $self->push_lines(
1231             '',
1232             '# check that sub produces enveloped result',
1233             'unless (ref($_w_res) eq "ARRAY" && $_w_res->[0]) {',
1234             );
1235 96         236 $self->indent;
1236 96 50       393 if (log_is_trace) {
1237 0         0 $self->_add_module('Data::Dumper');
1238 0         0 $self->push_lines(
1239             'local $Data::Dumper::Purity = 1;',
1240             'local $Data::Dumper::Terse = 1;',
1241             'local $Data::Dumper::Indent = 0;',
1242             );
1243 0         0 $self->_err(500,
1244             qq['BUG: Sub $sub_name does not produce envelope: '.].
1245             qq[Data::Dumper::Dumper(\$_w_res)]);
1246             } else {
1247 96         600 $self->_err(500,
1248             qq['BUG: Sub $sub_name does not produce envelope']);
1249             }
1250 96         366 $self->unindent;
1251 96         235 $self->push_lines('}');
1252             }
1253             }
1254              
1255 104         313 my $use_eval = $self->_needs_eval;
1256 104 50       297 if ($use_eval) {
1257 0         0 $self->select_section('CLOSE_EVAL');
1258 0         0 $self->push_lines('return $_w_res;');
1259 0         0 $self->unindent;
1260 0         0 $self->_add_var('_w_eval_err');
1261 0         0 $self->push_lines(
1262             '};',
1263             '$_w_eval_err = $@;');
1264              
1265             # _needs_eval will automatically be enabled here, due after_eval being
1266             # filled
1267 0         0 $self->select_section('after_eval');
1268 0         0 $self->push_lines('warn $_w_eval_err if $_w_eval_err;');
1269 0         0 $self->_errif(500, '"Function died: $_w_eval_err"', '$_w_eval_err');
1270              
1271 0         0 $self->select_section('OPEN_EVAL');
1272 0         0 $self->push_lines('eval {');
1273 0         0 $self->indent;
1274             }
1275              
1276             # return sub result
1277 104         293 $self->select_section('BEFORE_CLOSE_SUB');
1278 104 100       368 $self->push_lines('return $_w_res;') if $needs_store_res;
1279 104         277 $self->select_section('CLOSE_SUB');
1280 104         240 $self->unindent;
1281 104         291 $self->push_lines('}'); # wrapper sub
1282              
1283             # return wrap result
1284 104         674 my $result = {
1285             sub_name => $sub_name,
1286             sub_ref_name => $sub_ref_name,
1287             meta => $meta,
1288             meta_name => $meta_name,
1289             use_eval => $use_eval,
1290             };
1291 104 100       305 if ($args{embed}) {
1292 52         238 $result->{source} = $self->_format_embed_wrapper_code;
1293             } else {
1294 52         222 my $source = $self->_format_dyn_wrapper_code;
1295 52 50 33     368 if ($Log_Wrapper_Code && log_is_trace()) {
1296 0         0 require String::LineNumber;
1297             log_trace("wrapper code:\n%s",
1298 0 0 0     0 $ENV{LINENUM} // 1 ?
1299             String::LineNumber::linenum($source) :
1300             $source);
1301             }
1302 52         126 $result->{source} = $source;
1303 52 50       170 if ($args{compile}) {
1304 52     8   6240 my $wrapped = eval $source;
  8     8   58  
  8         16  
  8         76  
  8         587  
  8         17  
  8         2246  
1305 52 50 33     367 die "BUG: Wrapper code can't be compiled: $@" if $@ || !$wrapped;
1306 52         166 $result->{sub} = $wrapped;
1307             }
1308             }
1309              
1310 104         1081 [200, "OK", $result];
1311             }
1312              
1313             $SPEC{wrap_sub} = {
1314             v => 1.1,
1315             summary => 'Wrap subroutine to do various things, '.
1316             'like enforcing Rinci properties',
1317             result => {
1318             summary => 'The wrapped subroutine along with its new metadata',
1319             description => <<'_',
1320              
1321             Aside from wrapping the subroutine, the wrapper will also create a new metadata
1322             for the subroutine. The new metadata is a clone of the original, with some
1323             properties changed, e.g. schema in `args` and `result` normalized, some values
1324             changed according to the `convert` argument, some defaults set, etc.
1325              
1326             The new metadata will also contain (or append) the wrapping log located in the
1327             `x.perinci.sub.wrapper.logs` attribute. The wrapping log marks that the wrapper
1328             has added some functionality (like validating arguments or result) so that
1329             future nested wrapper can choose to avoid duplicating the same functionality.
1330              
1331             _
1332             schema=>['hash*'=>{keys=>{
1333             sub=>'code*',
1334             source=>['any*' => of => ['str*', ['hash*' => each_value=>'str*']]],
1335             meta=>'hash*',
1336             }}],
1337             },
1338             args => {
1339             sub => {
1340             schema => 'str*',
1341             summary => 'The code to be wrapped',
1342             description => <<'_',
1343              
1344             At least one of `sub` or `sub_name` must be specified.
1345              
1346             _
1347             },
1348             sub_name => {
1349             schema => 'str*',
1350             summary => 'The name of the subroutine, '.
1351             'e.g. func or Foo::func (qualified)',
1352             description => <<'_',
1353              
1354             At least one of `sub` or `sub_name` must be specified.
1355              
1356             _
1357             },
1358             meta => {
1359             schema => 'hash*',
1360             summary => 'The function metadata',
1361             req => 1,
1362             },
1363             meta_name => {
1364             schema => 'str*',
1365             summary => 'Where to find the metadata, e.g. "$SPEC{foo}"',
1366             description => <<'_',
1367              
1368             Some wrapper code (e.g. handler for `dep` property) needs to refer to the
1369             function metadata. If not provided, the wrapper will store the function metadata
1370             in a unique variable (e.g. `$Perinci::Sub::Wrapped::meta34127816`).
1371              
1372             _
1373             },
1374             convert => {
1375             schema => 'hash*',
1376             summary => 'Properties to convert to new value',
1377             description => <<'_',
1378              
1379             Not all properties can be converted, but these are a partial list of those that
1380             can: v (usually do not need to be specified when converting from 1.0 to 1.1,
1381             will be done automatically), args_as, result_naked, default_lang.
1382              
1383             _
1384             },
1385             compile => {
1386             schema => ['bool' => {default=>1}],
1387             summary => 'Whether to compile the generated wrapper',
1388             description => <<'_',
1389              
1390             Can be set to 0 to not actually wrap but just return the generated wrapper
1391             source code.
1392              
1393             _
1394             },
1395             compile => {
1396             schema => ['bool' => {default=>1}],
1397             summary => 'Whether to compile the generated wrapper',
1398             description => <<'_',
1399              
1400             Can be set to 0 to not actually wrap but just return the generated wrapper
1401             source code.
1402              
1403             _
1404             },
1405             debug => {
1406             schema => [bool => {default=>0}],
1407             summary => 'Generate code with debugging',
1408             description => <<'_',
1409              
1410             If turned on, will produce various debugging in the generated code. Currently
1411             what this does:
1412              
1413             * add more comments (e.g. for each property handler)
1414              
1415             _
1416             },
1417             validate_args => {
1418             schema => ['bool'],
1419             summary => 'Whether wrapper should validate arguments',
1420             description => <<'_',
1421              
1422             If set to true, will validate arguments. Validation error will cause status 400
1423             to be returned. The default is to enable this unless previous wrapper(s) have
1424             already done this.
1425              
1426             _
1427             },
1428             validate_result => {
1429             schema => ['bool'],
1430             summary => 'Whether wrapper should validate arguments',
1431             description => <<'_',
1432              
1433             If set to true, will validate sub's result. Validation error will cause wrapper
1434             to return status 500 instead of sub's result. The default is to enable this
1435             unless previous wrapper(s) have already done this.
1436              
1437             _
1438             },
1439             core => {
1440             summary => 'If set to true, will avoid the use of non-core modules',
1441             schema => 'bool',
1442             },
1443             core_or_pp => {
1444             summary => 'If set to true, will avoid the use of non-core XS modules',
1445             schema => 'bool',
1446             description => <<'_',
1447              
1448             In other words, will stick to core or pure-perl modules only.
1449              
1450             _
1451             },
1452             pp => {
1453             summary => 'If set to true, will avoid the use of XS modules',
1454             schema => 'bool',
1455             },
1456             },
1457             };
1458             sub wrap_sub {
1459 112     112 1 663 __PACKAGE__->new->wrap(@_);
1460             }
1461              
1462             1;
1463             # ABSTRACT: A multi-purpose subroutine wrapping framework
1464              
1465             __END__
1466              
1467             =pod
1468              
1469             =encoding UTF-8
1470              
1471             =head1 NAME
1472              
1473             Perinci::Sub::Wrapper - A multi-purpose subroutine wrapping framework
1474              
1475             =head1 VERSION
1476              
1477             This document describes version 0.850 of Perinci::Sub::Wrapper (from Perl distribution Perinci-Sub-Wrapper), released on 2019-04-15.
1478              
1479             =head1 SYNOPSIS
1480              
1481             For dynamic usage:
1482              
1483             use Perinci::Sub::Wrapper qw(wrap_sub);
1484             my $res = wrap_sub(sub_name => "mysub", meta=>{...});
1485             my ($wrapped_sub, $meta) = ($res->[2]{sub}, $res->[2]{meta});
1486             $wrapped_sub->(); # call the wrapped function
1487              
1488             =head1 DESCRIPTION
1489              
1490             Perinci::Sub::Wrapper (PSW for short) is an extensible subroutine wrapping
1491             framework. It generates code to do stuffs before calling your subroutine, like
1492             validate arguments, convert arguments from positional/array to named/hash or
1493             vice versa, etc; as well as generate code to do stuffs after calling your
1494             subroutine, like retry calling for a number of times if subroutine returns a
1495             non-success status, check subroutine result against a schema, etc). Some other
1496             things it can do: apply a timeout, currying, and so on.
1497              
1498             PSW differs from other function composition or decoration system like Python
1499             decorators (or its Perl equivalent L<Python::Decorator>) in a couple of ways:
1500              
1501             =over
1502              
1503             =item * Single wrapper
1504              
1505             Instead of multiple/nested wrapping for implementing different features, PSW
1506             is designed to generate a single large wrapper around your code, i.e.:
1507              
1508             sub _wrapper_for_your_sub {
1509             ...
1510             # do various stuffs before calling:
1511              
1512             # e.g. start timer
1513             # e.g. convert, prefill, validate arguments
1514             my @args = ...;
1515             ...
1516             your_sub(@args);
1517             ...
1518             # do various stuffs after calling
1519             ...
1520             # e.g. report times
1521             # e.g. perform retry
1522             # e.g. convert or envelope results
1523              
1524             # return result
1525             }
1526              
1527             Multiple functionalities will be added and combined in this single wrapper
1528             subroutine in the appropriate location. This is done to reduce function call
1529             overhead or depth of nested call levels. And also to make it easier to embed the
1530             wrapping code to your source code (see L<Dist::Zilla::Plugin::Rinci::Wrap>).
1531              
1532             Of course, you can still wrap multiple times if wanted.
1533              
1534             =item * Rinci
1535              
1536             The wrapper code is built according to the L<Rinci> metadata you provide. Rinci
1537             allows you to specify various things for your function, e.g. list of arguments
1538             including the expected data type of each argument and whether an argument is
1539             required or optional. PSW can then be used to generate the necessary code to
1540             enforce this specification, e.g. generate validator for the function arguments.
1541              
1542             Since Rinci specification is extensible, you can describe additional stuffs for
1543             your function and write a PSW plugin to generate the necessary code to implement
1544             your specification. An example is C<timeout> to specify execution time limit,
1545             implemented by L<Perinci::Sub::Property::timeout> which generates code to call
1546             function inside an C<eval()> block and use C<alarm()> to limit the execution.
1547             Another example is C<retry> property, implemented by
1548             L<Perinci::Sub::Property::retry> which generates code to call function inside a
1549             simple retry loop.
1550              
1551             =back
1552              
1553             Normally you do not use PSW directly in your applications. You might want to
1554             check out L<Perinci::Access::Perl> and L<Perinci::Exporter> on examples of
1555             wrapping function dynamically (during runtime), or
1556             L<Dist::Zilla::Plugin::Rinci::Wrap> on an example of embedding the generated
1557             wrapping code to source code during build.
1558              
1559             =head1 EXTENDING
1560              
1561             The framework is simple and extensible. Please delve directly into the source
1562             code for now. Some notes:
1563              
1564             The internal uses OO.
1565              
1566             The main wrapper building mechanism is in the C<wrap()> method.
1567              
1568             For each Rinci property, it will call C<handle_NAME()> wrapper handler method.
1569             The C<handlemeta_NAME()> methods are called first, to determine order of
1570             processing. You can supply these methods either by subclassing the class or,
1571             more simply, monkeypatching the method in the C<Perinci::Sub::Wrapper> package.
1572              
1573             The wrapper handler method will be called with a hash argument, containing these
1574             keys: B<value> (property value), B<new> (this key will exist if C<convert>
1575             argument of C<wrap()> exists, to convert a property to a new value).
1576              
1577             For properties that have name in the form of C<NAME1.NAME2.NAME3> (i.e., dotted)
1578             only the first part of the name will be used (i.e., C<handle_NAME1()>).
1579              
1580             =head1 VARIABLES
1581              
1582             =head2 $Log_Wrapper_Code (BOOL)
1583              
1584             Whether to log wrapper result. Default is from environment variable
1585             LOG_PERINCI_WRAPPER_CODE, or false. Logging is done with L<Log::ger> at trace
1586             level.
1587              
1588             =head1 RINCI FUNCTION METADATA
1589              
1590             =head2 x.perinci.sub.wrapper.disable_validate_args => bool
1591              
1592             Can be set to 1 to set C<validate_args> to 0 by default. This is used e.g. if
1593             you already embed/insert code to validate arguments by other means and do not
1594             want to repeat validating arguments. E.g. used if you use
1595             L<Dist::Zilla::Plugin::Rinci::Validate>.
1596              
1597             =head2 x.perinci.sub.wrapper.disable_validate_result => bool
1598              
1599             Can be set to 1 to set C<validate_result> to 0 by default. This is used e.g. if
1600             you already embed/insert code to validate result by other means and do not want
1601             to repeat validating result. E.g. used if you use
1602             L<Dist::Zilla::Plugin::Rinci::Validate>.
1603              
1604             =head2 x.perinci.sub.wrapper.logs => array
1605              
1606             Generated/added by this module to the function metadata for every wrapping done.
1607             Used to avoid adding repeated code, e.g. to validate result or arguments.
1608              
1609             =head1 PERFORMANCE NOTES
1610              
1611             The following numbers are produced on an Intel Core i5-2400 3.1GHz desktop using
1612             PSW v0.51 and Perl v5.18.2. Operating system is Debian sid (64bit).
1613              
1614             For perspective, empty subroutine (C<< sub {} >>) as well as C<< sub { [200,
1615             "OK"] } >> can be called around 5.3 mil/sec.
1616              
1617             Wrapping this subroutine C<< sub { [200, "OK"] } >> and this simple metadata C<<
1618             {v=>1.1} >> using default options yields call performance for C<< $sub->() >> of
1619             about 0.9 mil/sec. With C<< validate_args=>0 >> and C<< validate_result=>0 >>,
1620             it's 1.5 mil/sec.
1621              
1622             As more (and more complex) arguments are introduced and validated, overhead will
1623             increase. The significant portion of the overhead is in argument validation. For
1624             example, this metadata C<< {v=>1.1, args=>{a=>{schema=>"int"}}} >> yields 0.5
1625             mil/sec.
1626              
1627             =head1 FUNCTIONS
1628              
1629              
1630             =head2 wrap_sub
1631              
1632             Usage:
1633              
1634             wrap_sub(%args) -> [status, msg, payload, meta]
1635              
1636             Wrap subroutine to do various things, like enforcing Rinci properties.
1637              
1638             This function is not exported by default, but exportable.
1639              
1640             Arguments ('*' denotes required arguments):
1641              
1642             =over 4
1643              
1644             =item * B<compile> => I<bool> (default: 1)
1645              
1646             Whether to compile the generated wrapper.
1647              
1648             Can be set to 0 to not actually wrap but just return the generated wrapper
1649             source code.
1650              
1651             =item * B<convert> => I<hash>
1652              
1653             Properties to convert to new value.
1654              
1655             Not all properties can be converted, but these are a partial list of those that
1656             can: v (usually do not need to be specified when converting from 1.0 to 1.1,
1657             will be done automatically), args_as, result_naked, default_lang.
1658              
1659             =item * B<core> => I<bool>
1660              
1661             If set to true, will avoid the use of non-core modules.
1662              
1663             =item * B<core_or_pp> => I<bool>
1664              
1665             If set to true, will avoid the use of non-core XS modules.
1666              
1667             In other words, will stick to core or pure-perl modules only.
1668              
1669             =item * B<debug> => I<bool> (default: 0)
1670              
1671             Generate code with debugging.
1672              
1673             If turned on, will produce various debugging in the generated code. Currently
1674             what this does:
1675              
1676             =over
1677              
1678             =item * add more comments (e.g. for each property handler)
1679              
1680             =back
1681              
1682             =item * B<meta>* => I<hash>
1683              
1684             The function metadata.
1685              
1686             =item * B<meta_name> => I<str>
1687              
1688             Where to find the metadata, e.g. "$SPEC{foo}".
1689              
1690             Some wrapper code (e.g. handler for C<dep> property) needs to refer to the
1691             function metadata. If not provided, the wrapper will store the function metadata
1692             in a unique variable (e.g. C<$Perinci::Sub::Wrapped::meta34127816>).
1693              
1694             =item * B<pp> => I<bool>
1695              
1696             If set to true, will avoid the use of XS modules.
1697              
1698             =item * B<sub> => I<str>
1699              
1700             The code to be wrapped.
1701              
1702             At least one of C<sub> or C<sub_name> must be specified.
1703              
1704             =item * B<sub_name> => I<str>
1705              
1706             The name of the subroutine, e.g. func or Foo::func (qualified).
1707              
1708             At least one of C<sub> or C<sub_name> must be specified.
1709              
1710             =item * B<validate_args> => I<bool>
1711              
1712             Whether wrapper should validate arguments.
1713              
1714             If set to true, will validate arguments. Validation error will cause status 400
1715             to be returned. The default is to enable this unless previous wrapper(s) have
1716             already done this.
1717              
1718             =item * B<validate_result> => I<bool>
1719              
1720             Whether wrapper should validate arguments.
1721              
1722             If set to true, will validate sub's result. Validation error will cause wrapper
1723             to return status 500 instead of sub's result. The default is to enable this
1724             unless previous wrapper(s) have already done this.
1725              
1726             =back
1727              
1728             Returns an enveloped result (an array).
1729              
1730             First element (status) is an integer containing HTTP status code
1731             (200 means OK, 4xx caller error, 5xx function error). Second element
1732             (msg) is a string containing error message, or 'OK' if status is
1733             200. Third element (payload) is optional, the actual result. Fourth
1734             element (meta) is called result metadata and is optional, a hash
1735             that contains extra information.
1736              
1737             Return value: The wrapped subroutine along with its new metadata (hash)
1738              
1739              
1740             Aside from wrapping the subroutine, the wrapper will also create a new metadata
1741             for the subroutine. The new metadata is a clone of the original, with some
1742             properties changed, e.g. schema in C<args> and C<result> normalized, some values
1743             changed according to the C<convert> argument, some defaults set, etc.
1744              
1745             The new metadata will also contain (or append) the wrapping log located in the
1746             C<x.perinci.sub.wrapper.logs> attribute. The wrapping log marks that the wrapper
1747             has added some functionality (like validating arguments or result) so that
1748             future nested wrapper can choose to avoid duplicating the same functionality.
1749              
1750             =for Pod::Coverage ^(new|handle(meta)?_.+|wrap|add_.+|section_empty|indent|unindent|get_indent_level|select_section|push_lines)$
1751              
1752             =head1 METHODS
1753              
1754             The OO interface is only used internally or when you want to extend the wrapper.
1755              
1756             =head1 FAQ
1757              
1758             =head2 General
1759              
1760             =over
1761              
1762             =item * What is a function wrapper?
1763              
1764             A wrapper function calls the target function but with additional behaviors. The
1765             goal is similar to function composition or decorator system like in Python (or
1766             its Perl equivalent L<Python::Decorator>) where you use a higher-order function
1767             which accepts another function and modifies it.
1768              
1769             It is used to add various functionalities, e.g.: cache/memoization, singleton,
1770             adding benchmarking/timing around function call, logging, argument validation
1771             (parameter checking), checking pre/post-condition, authentication/authorization
1772             checking, etc. The Python folks use decorators quite a bit; see discussions on
1773             the Internet on those.
1774              
1775             =item * How is PSW different from Python::Decorator?
1776              
1777             PSW uses dynamic code generation (it generates Perl code on the fly). It also
1778             creates a single large wrapper instead of nested wrappers. It builds wrapper
1779             code according to L<Rinci> specification.
1780              
1781             =item * Why use code generation?
1782              
1783             Mainly because L<Data::Sah>, which is the module used to do argument validation,
1784             also uses code generation. Data::Sah allows us to do data validation at full
1785             Perl speed, which can be one or two orders of magnitude faster than
1786             "interpreter" modules like L<Data::FormValidator>.
1787              
1788             =item * Why use a single large wrapper?
1789              
1790             This is just a design approach. It can impose some restriction for wrapper code
1791             authors, since everything needs to be put in a single subroutine, but has nice
1792             properties like less stack trace depth and less function call overhead.
1793              
1794             =back
1795              
1796             =head2 Debugging
1797              
1798             =over
1799              
1800             =item * How to display the wrapper code being generated?
1801              
1802             If environment variable L<LOG_PERINCI_WRAPPER_CODE> or package variable
1803             $Log_Perinci_Wrapper_Code is set to true, generated wrapper source code is
1804             logged at trace level using L<Log::ger>. It can be displayed, for example:
1805              
1806             % LOG_PERINCI_WRAPPER_CODE=1 TRACE=1 \
1807             perl -MLog::ger::LevelFromEnv -MLog::ger::Output=Screen \
1808             -MPerinci::Sub::Wrapper=wrap_sub \
1809             -e 'wrap_sub(sub=>sub{}, meta=>{v=>1.1, args=>{a=>{schema=>"int"}}});'
1810              
1811             Note that L<Data::Sah> (the module used to generate validator code) observes
1812             C<LOG_SAH_VALIDATOR_CODE>, but during wrapping this environment flag is
1813             currently disabled by this module, so you need to set
1814             L<LOG_PERINCI_WRAPPER_CODE> instead.
1815              
1816             =back
1817              
1818             =head2 caller() doesn't work from inside my wrapped code!
1819              
1820             Wrapping adds at least one or two levels of calls: one for the wrapper
1821             subroutine itself, the other is for the eval trap when necessary.
1822              
1823             This poses a problem if you need to call caller() from within your wrapped code;
1824             it will also be off by at least one or two.
1825              
1826             The solution is for your function to use the caller() replacement, provided by
1827             L<Perinci::Sub::Util>. Or use embedded mode, where the wrapper code won't add
1828             extra subroutine calls.
1829              
1830             =head1 ENVIRONMENT
1831              
1832             =head2 LOG_PERINCI_WRAPPER_CODE (bool)
1833              
1834             If set to 1, will log the generated wrapper code. This value is used to set
1835             C<$Log_Wrapper_Code> if it is not already set.
1836              
1837             =head2 PERINCI_WRAPPER_CORE => bool
1838              
1839             Set default for wrap argument C<core>.
1840              
1841             =head2 PERINCI_WRAPPER_CORE_OR_PP => bool
1842              
1843             Set default for wrap argument C<core_or_pp>.
1844              
1845             =head2 PERINCI_WRAPPER_PP => bool
1846              
1847             Set default for wrap argument C<pp>.
1848              
1849             =head1 HOMEPAGE
1850              
1851             Please visit the project's homepage at L<https://metacpan.org/release/Perinci-Sub-Wrapper>.
1852              
1853             =head1 SOURCE
1854              
1855             Source repository is at L<https://github.com/perlancar/perl-Perinci-Sub-Wrapper>.
1856              
1857             =head1 BUGS
1858              
1859             Please report any bugs or feature requests on the bugtracker website L<https://rt.cpan.org/Public/Dist/Display.html?Name=Perinci-Sub-Wrapper>
1860              
1861             When submitting a bug or request, please include a test-file or a
1862             patch to an existing test-file that illustrates the bug or desired
1863             feature.
1864              
1865             =head1 SEE ALSO
1866              
1867             L<Perinci>, L<Rinci>
1868              
1869             L<Python::Decorator>
1870              
1871             L<Dist::Zilla::Plugin::Rinci::Wrap>
1872              
1873             L<Dist::Zilla::Plugin::Rinci::Validate>
1874              
1875             =head1 AUTHOR
1876              
1877             perlancar <perlancar@cpan.org>
1878              
1879             =head1 COPYRIGHT AND LICENSE
1880              
1881             This software is copyright (c) 2019, 2017, 2016, 2015, 2014, 2013, 2012, 2011 by perlancar@cpan.org.
1882              
1883             This is free software; you can redistribute it and/or modify it under
1884             the same terms as the Perl 5 programming language system itself.
1885              
1886             =cut