File Coverage

blib/lib/Perinci/Sub/Util.pm
Criterion Covered Total %
statement 139 176 78.9
branch 50 82 60.9
condition 20 40 50.0
subroutine 15 18 83.3
pod 6 6 100.0
total 230 322 71.4


line stmt bran cond sub pod time code
1             package Perinci::Sub::Util;
2              
3 5     5   917024 use 5.010001;
  5         24  
4 5     5   72 use strict;
  5         12  
  5         231  
5 5     5   32 use warnings;
  5         9  
  5         313  
6 5     5   10741 use Log::ger;
  5         301  
  5         29  
7              
8 5     5   1536 use Exporter qw(import);
  5         8  
  5         1997  
9              
10             our $AUTHORITY = 'cpan:PERLANCAR'; # AUTHORITY
11             our $DATE = '2023-10-28'; # DATE
12             our $DIST = 'Perinci-Sub-Util'; # DIST
13             our $VERSION = '0.472'; # VERSION
14              
15             our @EXPORT_OK = qw(
16             err
17             caller
18             warn_err
19             die_err
20             gen_modified_sub
21             gen_curried_sub
22             );
23              
24             our %SPEC;
25              
26             $SPEC{':package'} = {
27             v => 1.1,
28             summary => 'Helper when writing functions',
29             };
30              
31             our $STACK_TRACE;
32             our @_c; # to store temporary celler() result
33             our $_i; # temporary variable
34             sub err {
35 7     7 1 472140 require Scalar::Util;
36              
37             # get information about caller
38 7         44 my @caller = CORE::caller(1);
39 7 50       29 if (!@caller) {
40             # probably called from command-line (-e)
41 0         0 @caller = ("main", "-e", 1, "program");
42             }
43              
44 7         14 my ($status, $msg, $meta, $prev);
45              
46 7         21 for (@_) {
47 7         18 my $ref = ref($_);
48 7 100       29 if ($ref eq 'ARRAY') { $prev = $_ }
  2 50       8  
    50          
49 0         0 elsif ($ref eq 'HASH') { $meta = $_ }
50             elsif (!$ref) {
51 5 100       25 if (Scalar::Util::looks_like_number($_)) {
52 3         8 $status = $_;
53             } else {
54 2         6 $msg = $_;
55             }
56             }
57             }
58              
59 7   100     31 $status //= 500;
60 7   66     35 $msg //= "$caller[3] failed";
61 7   50     41 $meta //= {};
62 7 100 33     56 $meta->{prev} //= $prev if $prev;
63              
64             # put information on who produced this error and where/when
65 7 50       25 if (!$meta->{logs}) {
66              
67             # should we produce a stack trace?
68 7         10 my $stack_trace;
69             {
70 5     5   41 no warnings;
  5         20  
  5         5727  
  7         12  
71             # we use Carp::Always as a sign that user wants stack traces
72 7 100 66     56 last unless $STACK_TRACE // $INC{"Carp/Always.pm"};
73             # stack trace is already there in previous result's log
74             last if $prev && ref($prev->[3]) eq 'HASH' &&
75             ref($prev->[3]{logs}) eq 'ARRAY' &&
76             ref($prev->[3]{logs}[0]) eq 'HASH' &&
77 2 50 66     27 $prev->[3]{logs}[0]{stack_trace};
      66        
      33        
      33        
78 1         3 $stack_trace = [];
79 1         3 $_i = 1;
80 1         2 while (1) {
81             {
82 9         17 package DB;
83 9         51 @_c = CORE::caller($_i);
84 9 100       22 if (@_c) {
85 8         21 $_c[4] = [@DB::args];
86             }
87             }
88 9 100       22 last unless @_c;
89 8         28 push @$stack_trace, [@_c];
90 8         12 $_i++;
91             }
92             }
93 7         10 push @{ $meta->{logs} }, {
  7         70  
94             type => 'create',
95             time => time(),
96             package => $caller[0],
97             file => $caller[1],
98             line => $caller[2],
99             func => $caller[3],
100             ( stack_trace => $stack_trace ) x !!$stack_trace,
101             };
102             }
103              
104             #die;
105 7         57 [$status, $msg, undef, $meta];
106             }
107              
108             sub warn_err {
109 0     0 1 0 require Carp;
110              
111 0         0 my $res = err(@_);
112 0         0 Carp::carp("ERROR $res->[0]: $res->[1]");
113             }
114              
115             sub die_err {
116 0     0 1 0 require Carp;
117              
118 0         0 my $res = err(@_);
119 0         0 Carp::croak("ERROR $res->[0]: $res->[1]");
120             }
121              
122             sub caller {
123 0     0 1 0 my $n0 = shift;
124 0   0     0 my $n = $n0 // 0;
125              
126 0   0     0 my $pkg = $Perinci::Sub::Wrapper::default_wrapped_package //
127             'Perinci::Sub::Wrapped';
128              
129 0         0 my @r;
130 0         0 my $i = 0;
131 0         0 my $j = -1;
132 0         0 while ($i <= $n+1) { # +1 for this sub itself
133 0         0 $j++;
134 0         0 @r = CORE::caller($j);
135 0 0       0 last unless @r;
136 0 0 0     0 if ($r[0] eq $pkg && $r[1] =~ /^\(eval /) {
137 0         0 next;
138             }
139 0         0 $i++;
140             }
141              
142 0 0       0 return unless @r;
143 0 0       0 return defined($n0) ? @r : $r[0];
144             }
145              
146             $SPEC{gen_modified_sub} = {
147             v => 1.1,
148             summary => 'Generate modified metadata (and subroutine) based on another',
149             description => <<'_',
150              
151             Often you'll want to create another sub (and its metadata) based on another, but
152             with some modifications, e.g. add/remove/rename some arguments, change summary,
153             add/remove some properties, and so on.
154              
155             Instead of cloning the Rinci metadata and modify it manually yourself, this
156             routine provides some shortcuts.
157              
158             You can specify base sub/metadata using `base_name` (string, subroutine name,
159             either qualified or not) or `base_code` (coderef) + `base_meta` (hash).
160              
161             _
162             args => {
163             die => {
164             summary => 'Die upon failure',
165             schema => 'bool*',
166             },
167              
168             base_name => {
169             summary => 'Subroutine name (either qualified or not)',
170             schema => 'str*',
171             description => <<'_',
172              
173             If not qualified with package name, will be searched in the caller's package.
174             Rinci metadata will be searched in `%SPEC` package variable.
175              
176             Alternatively, you can also specify `base_code` and `base_meta`.
177              
178             Either `base_name` or `base_code` + `base_meta` are required.
179              
180             _
181             },
182             base_code => {
183             summary => 'Base subroutine code',
184             schema => 'code*',
185             description => <<'_',
186              
187             If you specify this, you'll also need to specify `base_meta`.
188              
189             Alternatively, you can specify `base_name` instead, to let this routine search
190             the base subroutine from existing Perl package.
191              
192             _
193             },
194             base_meta => {
195             summary => 'Base Rinci metadata',
196             schema => 'hash*', # XXX defhash/rifunc
197             },
198             output_name => {
199             summary => 'Where to install the modified sub',
200             schema => 'str*',
201             description => <<'_',
202              
203             Output subroutine will be put in the specified name. If the name is not
204             qualified with package name, will use caller's package. If the name is not
205             specified, the base name will be used and must not be from the caller's package.
206              
207             Note that this argument is optional.
208              
209             To prevent installing subroutine, set `install_sub` to false.
210             _
211             },
212             output_code => {
213             summary => 'Code for the modified sub',
214             schema => 'code*',
215             description => <<'_',
216              
217             Alternatively you can use `wrap_code`. If both are not specified, will use
218             `base_code` (which will then be required) as the modified subroutine's code.
219              
220             _
221             },
222             wrap_code => {
223             summary => 'Wrapper to generate the modified sub',
224             schema => 'code*',
225             description => <<'_',
226              
227             The modified sub will become:
228              
229             sub { wrap_code->(base_code, @_) }
230              
231             Alternatively you can use `output_code`. If both are not specified, will use
232             `base_code` (which will then be required) as the modified subroutine's code.
233              
234             _
235             },
236             summary => {
237             summary => 'Summary for the mod subroutine',
238             schema => 'str*',
239             },
240             description => {
241             summary => 'Description for the mod subroutine',
242             schema => 'str*',
243             },
244             remove_args => {
245             summary => 'List of arguments to remove',
246             schema => 'array*',
247             },
248             add_args => {
249             summary => 'Arguments to add',
250             schema => 'hash*',
251             },
252             replace_args => {
253             summary => 'Arguments to add',
254             schema => 'hash*',
255             },
256             rename_args => {
257             summary => 'Arguments to rename',
258             schema => 'hash*',
259             },
260             modify_args => {
261             summary => 'Arguments to modify',
262             description => <<'_',
263              
264             For each argument you can specify a coderef. The coderef will receive the
265             argument ($arg_spec) and is expected to modify the argument specification.
266              
267             _
268             schema => 'hash*',
269             },
270             modify_meta => {
271             summary => 'Specify code to modify metadata',
272             schema => 'code*',
273             description => <<'_',
274              
275             Code will be called with arguments ($meta) where $meta is the cloned Rinci
276             metadata.
277              
278             _
279             },
280             install_sub => {
281             schema => 'bool',
282             default => 1,
283             },
284             },
285             args_rels => {
286             req_one => [qw/base_name base_code/],
287             choose_all => [qw/base_code base_meta/],
288             },
289             result => {
290             schema => ['hash*' => {
291             keys => {
292             code => ['code*'],
293             meta => ['hash*'], # XXX defhash/risub
294             },
295             }],
296             },
297             };
298             sub gen_modified_sub {
299 6     6 1 487276 require Function::Fallback::CoreOrPP;
300              
301 6         1374 my %args = @_;
302              
303             # get base code/meta
304 6         18 my $caller_pkg = CORE::caller();
305 6         26 my ($base_code, $base_meta);
306 6         0 my ($base_pkg, $base_leaf);
307 6 50       33 if ($args{base_name}) {
    0          
308 6 50       78 if ($args{base_name} =~ /(.+)::(.+)/) {
309 6         35 ($base_pkg, $base_leaf) = ($1, $2);
310             } else {
311 0         0 $base_pkg = $caller_pkg;
312 0         0 $base_leaf = $args{base_name};
313             }
314             {
315 5     5   44 no strict 'refs'; ## no critic: TestingAndDebugging::ProhibitNoStrict
  5         9  
  5         3751  
  6         13  
316 6         10 $base_code = \&{"$base_pkg\::$base_leaf"};
  6         32  
317 6         14 $base_meta = ${"$base_pkg\::SPEC"}{$base_leaf};
  6         28  
318             }
319 6 50       26 die "Can't find Rinci metadata for $base_pkg\::$base_leaf" unless $base_meta;
320             } elsif ($args{base_meta}) {
321 0         0 $base_meta = $args{base_meta};
322             $base_code = $args{base_code}
323 0 0       0 or die "Please specify base_code";
324             } else {
325 0         0 die "Please specify base_name or base_code+base_meta";
326             }
327              
328 6         29 my $output_meta = Function::Fallback::CoreOrPP::clone($base_meta);
329 1     1   1240 my $output_code = ($args{wrap_code} ? sub { $args{wrap_code}->($base_code, @_) } : undef) //
330 6 100 100     2339 $args{output_code} // $base_code;
      66        
331              
332             # modify metadata
333 6         18 for (qw/summary description/) {
334 12 100       56 $output_meta->{$_} = $args{$_} if $args{$_};
335             }
336 6 100       63 if ($args{remove_args}) {
337 3         5 delete $output_meta->{args}{$_} for @{ $args{remove_args} };
  3         13  
338             }
339 6 100       22 if ($args{add_args}) {
340 1         2 for my $k (keys %{ $args{add_args} }) {
  1         3  
341 1         2 my $v = $args{add_args}{$k};
342             die "Can't add arg '$k' in mod sub: already exists"
343 1 50       3 if $output_meta->{args}{$k};
344 1         3 $output_meta->{args}{$k} = $v;
345             }
346             }
347 6 100       20 if ($args{replace_args}) {
348 1         2 for my $k (keys %{ $args{replace_args} }) {
  1         3  
349 1         2 my $v = $args{replace_args}{$k};
350             die "Can't replace arg '$k' in mod sub: doesn't exist"
351 1 50       3 unless $output_meta->{args}{$k};
352 1         3 $output_meta->{args}{$k} = $v;
353             }
354             }
355 6 100       19 if ($args{rename_args}) {
356 1         2 for my $old (keys %{ $args{rename_args} }) {
  1         3  
357 1         3 my $new = $args{rename_args}{$old};
358 1         1 my $as = $output_meta->{args}{$old};
359 1 50       4 die "Can't rename arg '$old' in mod sub: doesn't exist" unless $as;
360             die "Can't rename arg '$old'->'$new' in mod sub: ".
361 1 50       3 "new name already exist" if $output_meta->{args}{$new};
362 1         3 $output_meta->{args}{$new} = $as;
363 1         2 delete $output_meta->{args}{$old};
364             }
365             }
366 6 100       18 if ($args{modify_args}) {
367 1         6 for (keys %{ $args{modify_args} }) {
  1         3  
368 1         5 $args{modify_args}{$_}->($output_meta->{args}{$_});
369             }
370             }
371 6 100       22 if ($args{modify_meta}) {
372 1         4 $args{modify_meta}->($output_meta);
373             }
374              
375             # install
376 6         20 my ($output_pkg, $output_leaf);
377 6 100       28 if (!defined $args{output_name}) {
    50          
378 4         7 $output_pkg = $caller_pkg;
379 4         10 $output_leaf = $base_leaf;
380 4 50       15 if ($base_pkg eq $output_pkg) {
381 0 0       0 if ($args{die}) {
382 0         0 die "Won't override $base_pkg\::$base_leaf";
383             } else {
384 0         0 return [412, "Won't override $base_pkg\::$base_leaf"];
385             }
386             }
387             } elsif ($args{output_name} =~ /(.+)::(.+)/) {
388 2         8 ($output_pkg, $output_leaf) = ($1, $2);
389             } else {
390 0         0 $output_pkg = $caller_pkg;
391 0         0 $output_leaf = $args{output_name};
392             }
393             {
394 5     5   48 no strict 'refs'; ## no critic: TestingAndDebugging::ProhibitNoStrict
  5         16  
  5         277  
  6         10  
395 5     5   35 no warnings 'redefine', 'once';
  5         8  
  5         3802  
396 6         45 log_trace "Installing modified sub to $output_pkg\::$output_leaf ...";
397 6 50 50     52 *{"$output_pkg\::$output_leaf"} = $output_code if $args{install_sub} // 1;
  6         42  
398 6         11 ${"$output_pkg\::SPEC"}{$output_leaf} = $output_meta;
  6         33  
399             }
400              
401 6         46 [200, "OK", {code=>$output_code, meta=>$output_meta}];
402             }
403              
404             $SPEC{gen_curried_sub} = {
405             v => 1.1,
406             summary => 'Generate curried subroutine (and its metadata)',
407             description => <<'_',
408              
409             This is a more convenient helper than `gen_modified_sub` if you want to create a
410             new subroutine that has some of its arguments preset (so they no longer need to
411             be present in the new metadata).
412              
413             For more general needs of modifying a subroutine (e.g. add some arguments,
414             modify some arguments, etc) use `gen_modified_sub`.
415              
416             _
417             args => {
418             base_name => {
419             summary => 'Subroutine name (either qualified or not)',
420             schema => 'str*',
421             description => <<'_',
422              
423             If not qualified with package name, will be searched in the caller's package.
424             Rinci metadata will be searched in `%SPEC` package variable.
425              
426             _
427             req => 1,
428             pos => 0,
429             },
430             set_args => {
431             summary => 'Arguments to set',
432             schema => 'hash*',
433             req => 1,
434             pos => 1,
435             },
436             output_name => {
437             summary => 'Where to install the modified sub',
438             schema => 'str*',
439             description => <<'_',
440              
441             Subroutine will be put in the specified name. If the name is not qualified with
442             package name, will use caller's package. If the name is not specified, will use
443             the base name which must not be in the caller's package.
444              
445             _
446             pos => 2,
447             },
448             },
449             args_as => 'array',
450             result_naked => 1,
451             };
452             sub gen_curried_sub {
453 2     2 1 382715 my ($base_name, $set_args, $output_name) = @_;
454              
455 2         13 my $caller = CORE::caller();
456              
457 2         4 my ($base_pkg, $base_leaf);
458 2 50       14 if ($base_name =~ /(.+)::(.+)/) {
459 2         8 ($base_pkg, $base_leaf) = ($1, $2);
460             } else {
461 0         0 $base_pkg = $caller;
462 0         0 $base_leaf = $base_name;
463             }
464              
465 2         3 my ($output_pkg, $output_leaf);
466 2 100       10 if (!defined $output_name) {
    50          
467 1 50       12 die "Won't override $base_pkg\::$base_leaf" if $base_pkg eq $caller;
468 1         2 $output_pkg = $caller;
469 1         2 $output_leaf = $base_leaf;
470             } elsif ($output_name =~ /(.+)::(.+)/) {
471 1         2 ($output_pkg, $output_leaf) = ($1, $2);
472             } else {
473 0         0 $output_pkg = $caller;
474 0         0 $output_leaf = $output_name;
475             }
476              
477 2         3 my $base_sub = \&{"$base_pkg\::$base_leaf"};
  2         9  
478              
479             gen_modified_sub(
480             die => 1,
481             base_name => "$base_pkg\::$base_leaf",
482             output_name => "$output_pkg\::$output_leaf",
483             output_code => sub {
484 5     5   44 no strict 'refs'; ## no critic: TestingAndDebugging::ProhibitNoStrict
  5         12  
  5         1160  
485 2     2   16 $base_sub->(@_, %$set_args);
486             },
487 2         23 remove_args => [keys %$set_args],
488             install => 1,
489             );
490             }
491              
492             1;
493             # ABSTRACT: Helper when writing functions
494              
495             __END__
496              
497             =pod
498              
499             =encoding UTF-8
500              
501             =head1 NAME
502              
503             Perinci::Sub::Util - Helper when writing functions
504              
505             =head1 VERSION
506              
507             This document describes version 0.472 of Perinci::Sub::Util (from Perl distribution Perinci-Sub-Util), released on 2023-10-28.
508              
509             =head1 SYNOPSIS
510              
511             Example for err() and caller():
512              
513             use Perinci::Sub::Util qw(err caller);
514              
515             sub foo {
516             my %args = @_;
517             my $res;
518              
519             my $caller = caller();
520              
521             $res = bar(...);
522             return err($err, 500, "Can't foo") if $res->[0] != 200;
523              
524             [200, "OK"];
525             }
526              
527             Example for die_err() and warn_err():
528              
529             use Perinci::Sub::Util qw(warn_err die_err);
530             warn_err(403, "Forbidden");
531             die_err(403, "Forbidden");
532              
533             Example for gen_modified_sub():
534              
535             use Perinci::Sub::Util qw(gen_modified_sub);
536              
537             $SPEC{list_users} = {
538             v => 1.1,
539             args => {
540             search => {},
541             is_suspended => {},
542             },
543             };
544             sub list_users { ... }
545              
546             gen_modified_sub(
547             output_name => 'list_suspended_users',
548             base_name => 'list_users',
549             remove_args => ['is_suspended'],
550             output_code => sub {
551             list_users(@_, is_suspended=>1);
552             },
553             );
554              
555             Example for gen_curried_sub():
556              
557             use Perinci::Sub::Util qw(gen_curried_sub);
558              
559             $SPEC{list_users} = {
560             v => 1.1,
561             args => {
562             search => {},
563             is_suspended => {},
564             },
565             };
566             sub list_users { ... }
567              
568             # simpler/shorter than gen_modified_sub, but can be used for currying only
569             gen_curried_sub('list_users', {is_suspended=>1}, 'list_suspended_users');
570              
571             =head1 FUNCTIONS
572              
573              
574             =head2 gen_curried_sub
575              
576             Usage:
577              
578             gen_curried_sub($base_name, $set_args, $output_name) -> any
579              
580             Generate curried subroutine (and its metadata).
581              
582             This is a more convenient helper than C<gen_modified_sub> if you want to create a
583             new subroutine that has some of its arguments preset (so they no longer need to
584             be present in the new metadata).
585              
586             For more general needs of modifying a subroutine (e.g. add some arguments,
587             modify some arguments, etc) use C<gen_modified_sub>.
588              
589             This function is not exported by default, but exportable.
590              
591             Arguments ('*' denotes required arguments):
592              
593             =over 4
594              
595             =item * B<$base_name>* => I<str>
596              
597             Subroutine name (either qualified or not).
598              
599             If not qualified with package name, will be searched in the caller's package.
600             Rinci metadata will be searched in C<%SPEC> package variable.
601              
602             =item * B<$output_name> => I<str>
603              
604             Where to install the modified sub.
605              
606             Subroutine will be put in the specified name. If the name is not qualified with
607             package name, will use caller's package. If the name is not specified, will use
608             the base name which must not be in the caller's package.
609              
610             =item * B<$set_args>* => I<hash>
611              
612             Arguments to set.
613              
614              
615             =back
616              
617             Return value: (any)
618              
619              
620              
621             =head2 gen_modified_sub
622              
623             Usage:
624              
625             gen_modified_sub(%args) -> [$status_code, $reason, $payload, \%result_meta]
626              
627             Generate modified metadata (and subroutine) based on another.
628              
629             Often you'll want to create another sub (and its metadata) based on another, but
630             with some modifications, e.g. add/remove/rename some arguments, change summary,
631             add/remove some properties, and so on.
632              
633             Instead of cloning the Rinci metadata and modify it manually yourself, this
634             routine provides some shortcuts.
635              
636             You can specify base sub/metadata using C<base_name> (string, subroutine name,
637             either qualified or not) or C<base_code> (coderef) + C<base_meta> (hash).
638              
639             This function is not exported by default, but exportable.
640              
641             Arguments ('*' denotes required arguments):
642              
643             =over 4
644              
645             =item * B<add_args> => I<hash>
646              
647             Arguments to add.
648              
649             =item * B<base_code> => I<code>
650              
651             Base subroutine code.
652              
653             If you specify this, you'll also need to specify C<base_meta>.
654              
655             Alternatively, you can specify C<base_name> instead, to let this routine search
656             the base subroutine from existing Perl package.
657              
658             =item * B<base_meta> => I<hash>
659              
660             Base Rinci metadata.
661              
662             =item * B<base_name> => I<str>
663              
664             Subroutine name (either qualified or not).
665              
666             If not qualified with package name, will be searched in the caller's package.
667             Rinci metadata will be searched in C<%SPEC> package variable.
668              
669             Alternatively, you can also specify C<base_code> and C<base_meta>.
670              
671             Either C<base_name> or C<base_code> + C<base_meta> are required.
672              
673             =item * B<description> => I<str>
674              
675             Description for the mod subroutine.
676              
677             =item * B<die> => I<bool>
678              
679             Die upon failure.
680              
681             =item * B<install_sub> => I<bool> (default: 1)
682              
683             (No description)
684              
685             =item * B<modify_args> => I<hash>
686              
687             Arguments to modify.
688              
689             For each argument you can specify a coderef. The coderef will receive the
690             argument ($arg_spec) and is expected to modify the argument specification.
691              
692             =item * B<modify_meta> => I<code>
693              
694             Specify code to modify metadata.
695              
696             Code will be called with arguments ($meta) where $meta is the cloned Rinci
697             metadata.
698              
699             =item * B<output_code> => I<code>
700              
701             Code for the modified sub.
702              
703             Alternatively you can use C<wrap_code>. If both are not specified, will use
704             C<base_code> (which will then be required) as the modified subroutine's code.
705              
706             =item * B<output_name> => I<str>
707              
708             Where to install the modified sub.
709              
710             Output subroutine will be put in the specified name. If the name is not
711             qualified with package name, will use caller's package. If the name is not
712             specified, the base name will be used and must not be from the caller's package.
713              
714             Note that this argument is optional.
715              
716             To prevent installing subroutine, set C<install_sub> to false.
717              
718             =item * B<remove_args> => I<array>
719              
720             List of arguments to remove.
721              
722             =item * B<rename_args> => I<hash>
723              
724             Arguments to rename.
725              
726             =item * B<replace_args> => I<hash>
727              
728             Arguments to add.
729              
730             =item * B<summary> => I<str>
731              
732             Summary for the mod subroutine.
733              
734             =item * B<wrap_code> => I<code>
735              
736             Wrapper to generate the modified sub.
737              
738             The modified sub will become:
739              
740             sub { wrap_code->(base_code, @_) }
741              
742             Alternatively you can use C<output_code>. If both are not specified, will use
743             C<base_code> (which will then be required) as the modified subroutine's code.
744              
745              
746             =back
747              
748             Returns an enveloped result (an array).
749              
750             First element ($status_code) is an integer containing HTTP-like status code
751             (200 means OK, 4xx caller error, 5xx function error). Second element
752             ($reason) is a string containing error message, or something like "OK" if status is
753             200. Third element ($payload) is the actual result, but usually not present when enveloped result is an error response ($status_code is not 2xx). Fourth
754             element (%result_meta) is called result metadata and is optional, a hash
755             that contains extra information, much like how HTTP response headers provide additional metadata.
756              
757             Return value: (hash)
758              
759              
760             =head2 caller([ $n ])
761              
762             Just like Perl's builtin caller(), except that this one will ignore wrapper code
763             in the call stack. You should use this if your code is potentially wrapped. See
764             L<Perinci::Sub::Wrapper> for more details.
765              
766             =head2 err(...) => ARRAY
767              
768             Experimental.
769              
770             Generate an enveloped error response (see L<Rinci::function>). Can accept
771             arguments in an unordered fashion, by utilizing the fact that status codes are
772             always integers, messages are strings, result metadata are hashes, and previous
773             error responses are arrays. Error responses also seldom contain actual result.
774             Status code defaults to 500, status message will default to "FUNC failed". This
775             function will also fill the information in the C<logs> result metadata.
776              
777             Examples:
778              
779             err(); # => [500, "FUNC failed", undef, {...}];
780             err(404); # => [404, "FUNC failed", undef, {...}];
781             err(404, "Not found"); # => [404, "Not found", ...]
782             err("Not found", 404); # => [404, "Not found", ...]; # order doesn't matter
783             err([404, "Prev error"]); # => [500, "FUNC failed", undef,
784             # {logs=>[...], prev=>[404, "Prev error"]}]
785              
786             Will put C<stack_trace> in logs only if C<Carp::Always> module is loaded.
787              
788             =head2 warn_err(...)
789              
790             This is a shortcut for:
791              
792             $res = err(...);
793             warn "ERROR $res->[0]: $res->[1]";
794              
795             =head2 die_err(...)
796              
797             This is a shortcut for:
798              
799             $res = err(...);
800             die "ERROR $res->[0]: $res->[1]";
801              
802             =head1 FAQ
803              
804             =head2 What if I want to put result ($res->[2]) into my result with err()?
805              
806             You can do something like this:
807              
808             my $err = err(...) if ERROR_CONDITION;
809             $err->[2] = SOME_RESULT;
810             return $err;
811              
812             =head1 HOMEPAGE
813              
814             Please visit the project's homepage at L<https://metacpan.org/release/Perinci-Sub-Util>.
815              
816             =head1 SOURCE
817              
818             Source repository is at L<https://github.com/perlancar/perl-Perinci-Sub-Util>.
819              
820             =head1 SEE ALSO
821              
822             L<Perinci>
823              
824             =head1 AUTHOR
825              
826             perlancar <perlancar@cpan.org>
827              
828             =head1 CONTRIBUTOR
829              
830             =for stopwords Steven Haryanto
831              
832             Steven Haryanto <stevenharyanto@gmail.com>
833              
834             =head1 CONTRIBUTING
835              
836              
837             To contribute, you can send patches by email/via RT, or send pull requests on
838             GitHub.
839              
840             Most of the time, you don't need to build the distribution yourself. You can
841             simply modify the code, then test via:
842              
843             % prove -l
844              
845             If you want to build the distribution (e.g. to try to install it locally on your
846             system), you can install L<Dist::Zilla>,
847             L<Dist::Zilla::PluginBundle::Author::PERLANCAR>,
848             L<Pod::Weaver::PluginBundle::Author::PERLANCAR>, and sometimes one or two other
849             Dist::Zilla- and/or Pod::Weaver plugins. Any additional steps required beyond
850             that are considered a bug and can be reported to me.
851              
852             =head1 COPYRIGHT AND LICENSE
853              
854             This software is copyright (c) 2023, 2020, 2017, 2016, 2015, 2014 by perlancar <perlancar@cpan.org>.
855              
856             This is free software; you can redistribute it and/or modify it under
857             the same terms as the Perl 5 programming language system itself.
858              
859             =head1 BUGS
860              
861             Please report any bugs or feature requests on the bugtracker website L<https://rt.cpan.org/Public/Dist/Display.html?Name=Perinci-Sub-Util>
862              
863             When submitting a bug or request, please include a test-file or a
864             patch to an existing test-file that illustrates the bug or desired
865             feature.
866              
867             =cut