File Coverage

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


line stmt bran cond sub pod time code
1             package Sub::WrapInType;
2 6     6   1703272 use 5.010001;
  6         67  
3 6     6   219 use strict;
  6         12  
  6         149  
4 6     6   29 use warnings;
  6         15  
  6         207  
5 6     6   2647 use parent 'Exporter';
  6         1959  
  6         36  
6 6     6   3580 use Class::InsideOut qw( register readonly id );
  6         41795  
  6         41  
7 6     6   1453 use Types::Standard -types;
  6         115826  
  6         47  
8 6     6   49862 use Type::Params qw( signature );
  6         24427  
  6         64  
9 6     6   2017 use Sub::Util qw( set_subname );
  6         15  
  6         334  
10 6     6   2896 use namespace::autoclean;
  6         101959  
  6         34  
11              
12             our $VERSION = '0.08';
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             state $check = signature(
34             method => 1,
35             multi => [
36             +{
37             positional => [
38             $ParamsTypes,
39             $ReturnTypes,
40             CodeRef,
41 1     1   63 $Options, +{ default => sub { $DEFAULT_OPTIONS } }
42             ],
43             },
44             +{
45             named_to_list => 1,
46             named => [
47             params => $ParamsTypes,
48             isa => $ReturnTypes,
49             code => CodeRef,
50 1     1   114 options => $Options, +{ default => sub { $DEFAULT_OPTIONS } },
51 34     34 1 23308 ],
52             },
53             ],
54             );
55 34         185144 my ($class, $params_types, $return_types, $code, $options) = $check->(@_);
56 34         3677 $options = +{ %$DEFAULT_OPTIONS, %$options };
57              
58             my $typed_code =
59             $options->{check}
60             ? $class->_create_typed_code($params_types, $return_types, $code, $options)
61 34 100   9   195 : sub { $code->(@_) };
  9     4   229  
62              
63 34         90 my $self = bless $typed_code, $class;
64 34         140 register($self);
65              
66             {
67 34         543 my $addr = id $self;
  34         78  
68 34         70 $params{$addr} = $params_types;
69 34         66 $returns{$addr} = $return_types;
70 34         64 $code{$addr} = $code;
71 34         85 $is_method{$addr} = !!$options->{skip_invocant};
72             }
73              
74 34         397 $self;
75             }
76              
77             sub _create_typed_code {
78 25     25   69 my ($class, $params_types, $return_types, $code, $options) = @_;
79 25 100       145 my $params_types_checker =
    100          
80             ref $params_types eq 'ARRAY' ? signature(positional => $params_types)
81             : ref $params_types eq 'HASH' ? signature(named => [%$params_types], bless => 0)
82             : signature(positional => [$params_types]);
83 25 100       72267 my $return_types_checker =
84             ref $return_types eq 'ARRAY' ? signature(positional => $return_types)
85             : signature(positional => [$return_types]);
86              
87 25 100       57881 if ( ref $return_types eq 'ARRAY' ) {
88 3 100       17 if ( $options->{skip_invocant} ) {
89             sub {
90 1     1   9 my @return_values = $code->( shift, $params_types_checker->(@_) );
91 1         40 $return_types_checker->(@return_values);
92 1         19 @return_values;
93 1         13 };
94             }
95             else {
96             sub {
97 1     1   8 my @return_values = $code->( $params_types_checker->(@_) );
98 1         30 $return_types_checker->(@return_values);
99 1         22 @return_values;
100 2         13 };
101             }
102             }
103             else {
104 22 100       87 if ( $options->{skip_invocant} ) {
105             sub {
106 6     6   1805 my $return_value = $code->( shift, $params_types_checker->(@_) );
107 4         117 $return_types_checker->($return_value);
108 3         50 $return_value;
109 4         32 };
110             }
111             else {
112             sub {
113 13     15   3460 my $return_value = $code->( $params_types_checker->(@_) );
114 10         286 $return_types_checker->($return_value);
115 6         97 $return_value;
116 18         93 };
117             }
118             }
119             }
120              
121             sub _is_env_ndebug {
122 24 100   24   182 $ENV{PERL_NDEBUG} || $ENV{NDEBUG};
123             }
124              
125             sub wrap_sub {
126 22     22 1 29398 state $check = signature(
127             message => << 'EOS',
128             USAGE: wrap_sub(\@parameter_types, $return_type, $subroutine)
129             or wrap_sub(params => \@params_types, returns => $return_types, code => $subroutine)
130             EOS
131             multi => [
132             +{ positional => [ $ParamsTypes, $ReturnTypes, CodeRef ] },
133             +{
134             named_to_list => 1,
135             named => [
136             params => $ParamsTypes,
137             isa => $ReturnTypes,
138             code => CodeRef,
139             ],
140             },
141             ],
142             );
143 22         105324 my ($params_types, $return_types, $code) = $check->(@_);
144              
145 17         1356 __PACKAGE__->new($params_types, $return_types, $code, +{ check => !_is_env_ndebug() });
146             }
147              
148             sub wrap_method {
149 7     7 1 12505 state $check = signature(
150             message => << 'EOS',
151             USAGE: wrap_method(\@parameter_types, $return_type, $subroutine)
152             or wrap_method(params => \@params_types, returns => $return_types, code => $subroutine)
153             EOS
154             multi => [
155             +{ positional => [ $ParamsTypes, $ReturnTypes, CodeRef ] },
156             +{
157             named_to_list => 1,
158             named => [
159             params => $ParamsTypes,
160             isa => $ReturnTypes,
161             code => CodeRef,
162             ],
163             },
164             ],
165             );
166 7         104514 my ($params_types, $return_types, $code) = $check->(@_);
167              
168 7         562 my $options = +{
169             skip_invocant => 1,
170             check => !_is_env_ndebug(),
171             };
172 7         37 __PACKAGE__->new($params_types, $return_types, $code, $options);
173             }
174              
175             sub install_sub {
176 2     2 1 7921 state $check = signature(
177             message => << 'EOS',
178             USAGE: install_sub($name, \@parameter_types, $return_type, $subroutine)
179             or install_sub(name => $name, params => \@params_types, returns => $return_types, code => $subroutine)
180             EOS
181             multi => [
182             +{ positional => [ Str, $ParamsTypes, $ReturnTypes, CodeRef ] },
183             +{
184             named_to_list => 1,
185             named => [
186             name => Str,
187             params => $ParamsTypes,
188             isa => $ReturnTypes,
189             code => CodeRef,
190             ],
191             },
192             ],
193             );
194 2         99049 my ($name, $params_types, $return_types, $code) = $check->(@_);
195              
196 2         219 _install($name, wrap_sub($params_types, $return_types, $code), scalar caller);
197             }
198              
199             sub install_method {
200 2     2 1 8074 state $check = signature(
201             message => << 'EOS',
202             USAGE: install_method($name, \@parameter_types, $return_type, $subroutine)
203             or install_method(name => $name, params => \@params_types, returns => $return_types, code => $subroutine)
204             EOS
205             multi => [
206             +{ positional => [ Str, $ParamsTypes, $ReturnTypes, CodeRef ] },
207             +{
208             named => [
209             name => Str,
210             params => $ParamsTypes,
211             isa => $ReturnTypes,
212             code => CodeRef,
213             ],
214             named_to_list => 1,
215             },
216             ],
217             );
218 2         99322 my ($name, $params_types, $return_types, $code) = $check->(@_);
219              
220 2         234 _install($name, wrap_method($params_types, $return_types, $code), scalar caller);
221             }
222              
223             sub _install {
224 4     4   12 my ($name, $code, $pkg) = @_;
225 4         13 my $fullname = "${pkg}::${name}";
226             {
227 6     6   7607 no strict 'refs';
  6         15  
  6         478  
  4         6  
228 4         10 *{$fullname} = $code;
  4         21  
229             }
230 4         37 set_subname($fullname, $code);
231             }
232              
233             1;
234              
235             __END__