File Coverage

blib/lib/Sub/WrapInType.pm
Criterion Covered Total %
statement 102 102 100.0
branch 26 26 100.0
condition n/a
subroutine 26 26 100.0
pod 5 5 100.0
total 159 159 100.0


line stmt bran cond sub pod time code
1             package Sub::WrapInType;
2 6     6   1611051 use 5.010001;
  6         68  
3 6     6   33 use strict;
  6         14  
  6         131  
4 6     6   28 use warnings;
  6         21  
  6         208  
5 6     6   2781 use parent 'Exporter';
  6         1759  
  6         32  
6 6     6   3469 use Class::InsideOut qw( register readonly id );
  6         40753  
  6         45  
7 6     6   1323 use Types::Standard -types;
  6         88386  
  6         57  
8 6     6   32390 use Type::Params qw( multisig compile compile_named );
  6         59869  
  6         83  
9 6     6   2679 use Sub::Util qw( set_subname );
  6         15  
  6         374  
10 6     6   3068 use namespace::autoclean;
  6         102100  
  6         26  
11              
12             our $VERSION = '0.07';
13             our @EXPORT = qw( wrap_sub wrap_method install_sub install_method );
14              
15             readonly params => my %params;
16             readonly returns => my %returns;
17             readonly code => my %code;
18             readonly is_method => my %is_method;
19              
20             my $TypeConstraint = HasMethods[qw( check get_message )];
21             my $ParamsTypes = $TypeConstraint | ArrayRef[$TypeConstraint] | Map[Str, $TypeConstraint];
22             my $ReturnTypes = $TypeConstraint | ArrayRef[$TypeConstraint];
23             my $Options = Dict[
24             skip_invocant => Optional[Bool],
25             check => Optional[Bool],
26             ];
27             my $DEFAULT_OPTIONS = +{
28             skip_invocant => 0,
29             check => 1,
30             };
31              
32             sub new {
33 34     34 1 22829 my $class = shift;
34             state $check = multisig(
35 1     1   56 [ $ParamsTypes, $ReturnTypes, CodeRef, $Options, +{ default => sub { $DEFAULT_OPTIONS } } ],
36             compile_named(
37             params => $ParamsTypes,
38             isa => $ReturnTypes,
39             code => CodeRef,
40 1     1   86 options => $Options, +{ default => sub { $DEFAULT_OPTIONS } },
41 34         74 ),
42             );
43 34         66663 my ($params_types, $return_types, $code, $options) = do {
44 34         131 my @args = $check->(@_);
45 34 100       3242 ${^TYPE_PARAMS_MULTISIG} == 0 ? @args : @{ $args[0] }{qw( params isa code options )};
  9         37  
46             };
47 34         150 $options = +{ %$DEFAULT_OPTIONS, %$options };
48              
49             my $typed_code =
50             $options->{check}
51             ? $class->_create_typed_code($params_types, $return_types, $code, $options)
52 34 100   9   180 : sub { $code->(@_) };
  9     4   274  
53              
54 34         92 my $self = bless $typed_code, $class;
55 34         152 register($self);
56              
57             {
58 34         546 my $addr = id $self;
  34         86  
59 34         72 $params{$addr} = $params_types;
60 34         68 $returns{$addr} = $return_types;
61 34         58 $code{$addr} = $code;
62 34         84 $is_method{$addr} = !!$options->{skip_invocant};
63             }
64              
65 34         253 $self;
66             }
67              
68             sub _create_typed_code {
69 25     25   72 my ($class, $params_types, $return_types, $code, $options) = @_;
70 25 100       145 my $params_types_checker =
    100          
71             ref $params_types eq 'ARRAY' ? compile(@$params_types)
72             : ref $params_types eq 'HASH' ? compile_named(%$params_types)
73             : compile($params_types);
74 25 100       21453 my $return_types_checker =
75             ref $return_types eq 'ARRAY' ? compile(@$return_types) : compile($return_types);
76              
77 25 100       14132 if ( ref $return_types eq 'ARRAY' ) {
78 3 100       16 if ( $options->{skip_invocant} ) {
79             sub {
80 1     1   7 my @return_values = $code->( shift, $params_types_checker->(@_) );
81 1         31 $return_types_checker->(@return_values);
82 1         15 @return_values;
83 1         6 };
84             }
85             else {
86             sub {
87 1     1   8 my @return_values = $code->( $params_types_checker->(@_) );
88 1         26 $return_types_checker->(@return_values);
89 1         19 @return_values;
90 2         12 };
91             }
92             }
93             else {
94 22 100       74 if ( $options->{skip_invocant} ) {
95             sub {
96 6     6   1901 my $return_value = $code->( shift, $params_types_checker->(@_) );
97 4         104 $return_types_checker->($return_value);
98 3         42 $return_value;
99 4         26 };
100             }
101             else {
102             sub {
103 13     15   2755 my $return_value = $code->( $params_types_checker->(@_) );
104 10         313 $return_types_checker->($return_value);
105 6         90 $return_value;
106 18         102 };
107             }
108             }
109             }
110              
111             sub _is_env_ndebug {
112 24 100   24   188 $ENV{PERL_NDEBUG} || $ENV{NDEBUG};
113             }
114              
115             sub wrap_sub {
116 22     22 1 24074 state $check = multisig(
117             +{ message => << 'EOS' },
118             USAGE: wrap_sub(\@parameter_types, $return_type, $subroutine)
119             or wrap_sub(params => \@params_types, returns => $return_types, code => $subroutine)
120             EOS
121             [ $ParamsTypes, $ReturnTypes, CodeRef ],
122             compile_named(
123             params => $ParamsTypes,
124             isa => $ReturnTypes,
125             code => CodeRef,
126             ),
127             );
128 22         15879 my ($params_types, $return_types, $code) = do {
129 22         77 my @args = $check->(@_);
130 17 100       1199 ${^TYPE_PARAMS_MULTISIG} == 0 ? @args : @{ $args[0] }{qw( params isa code )};
  3         10  
131             };
132              
133 17         55 __PACKAGE__->new($params_types, $return_types, $code, +{ check => !_is_env_ndebug() });
134             }
135              
136             sub wrap_method {
137 7     7 1 11980 state $check = multisig(
138             +{ message => << 'EOS' },
139             USAGE: wrap_method(\@parameter_types, $return_type, $subroutine)
140             or wrap_method(params => \@params_types, returns => $return_types, code => $subroutine)
141             EOS
142             [ $ParamsTypes, $ReturnTypes, CodeRef ],
143             compile_named(
144             params => $ParamsTypes,
145             isa => $ReturnTypes,
146             code => CodeRef,
147             ),
148             );
149 7         16884 my ($params_types, $return_types, $code) = do {
150 7         34 my @args = $check->(@_);
151 7 100       533 ${^TYPE_PARAMS_MULTISIG} == 0 ? @args : @{ $args[0] }{qw( params isa code )};
  1         4  
152             };
153              
154 7         29 my $options = +{
155             skip_invocant => 1,
156             check => !_is_env_ndebug(),
157             };
158 7         45 __PACKAGE__->new($params_types, $return_types, $code, $options);
159             }
160              
161             sub install_sub {
162 2     2 1 6607 state $check = multisig(
163             +{ message => << 'EOS' },
164             USAGE: install_sub($name, \@parameter_types, $return_type, $subroutine)
165             or install_sub(name => $name, params => \@params_types, returns => $return_types, code => $subroutine)
166             EOS
167             [ Str, $ParamsTypes, $ReturnTypes, CodeRef ],
168             compile_named(
169             name => Str,
170             params => $ParamsTypes,
171             isa => $ReturnTypes,
172             code => CodeRef,
173             ),
174             );
175 2         13171 my ($name, $params_types, $return_types, $code) = do {
176 2         8 my @args = $check->(@_);
177 2 100       155 ${^TYPE_PARAMS_MULTISIG} == 0 ? @args : @{ $args[0] }{qw( name params isa code )};
  1         5  
178             };
179              
180 2         8 _install($name, wrap_sub($params_types, $return_types, $code), scalar caller);
181             }
182              
183             sub install_method {
184 2     2 1 8858 state $check = multisig(
185             +{ message => << 'EOS' },
186             USAGE: install_method($name, \@parameter_types, $return_type, $subroutine)
187             or install_method(name => $name, params => \@params_types, returns => $return_types, code => $subroutine)
188             EOS
189             [ Str, $ParamsTypes, $ReturnTypes, CodeRef ],
190             compile_named(
191             name => Str,
192             params => $ParamsTypes,
193             isa => $ReturnTypes,
194             code => CodeRef,
195             ),
196             );
197 2         16943 my ($name, $params_types, $return_types, $code) = do {
198 2         13 my @args = $check->(@_);
199 2 100       305 ${^TYPE_PARAMS_MULTISIG} == 0 ? @args : @{ $args[0] }{qw( name params isa code )};
  1         7  
200             };
201              
202 2         11 _install($name, wrap_method($params_types, $return_types, $code), scalar caller);
203             }
204              
205             sub _install {
206 4     4   14 my ($name, $code, $pkg) = @_;
207             {
208 6     6   8238 no strict 'refs';
  6         12  
  6         549  
  4         7  
209 4         7 *{"${pkg}::${name}"} = $code;
  4         25  
210             }
211 4         63 set_subname($name, $code);
212             }
213              
214             1;
215              
216             __END__