File Coverage

blib/lib/Type/Params.pm
Criterion Covered Total %
statement 200 200 100.0
branch 65 74 87.8
condition 39 63 61.9
subroutine 46 47 97.8
pod 14 14 100.0
total 364 398 91.4


line stmt bran cond sub pod time code
1             package Type::Params;
2              
3 72     72   514533 use 5.008001;
  72         298  
4 72     72   459 use strict;
  72         175  
  72         2152  
5 72     72   405 use warnings;
  72         186  
  72         6484  
6              
7             BEGIN {
8 72     72   250 $Type::Params::AUTHORITY = 'cpan:TOBYINK';
9 72         5540 $Type::Params::VERSION = '2.010001';
10             }
11              
12             $Type::Params::VERSION =~ tr/_//d;
13              
14 72     72   548 use B qw();
  72         233  
  72         2378  
15 72     72   17254 use Eval::TypeTiny qw( eval_closure set_subname );
  72         206  
  72         577  
16 72     72   49126 use Scalar::Util qw( refaddr );
  72         199  
  72         5660  
17 72     72   18063 use Error::TypeTiny;
  72         252  
  72         5352  
18 72     72   20903 use Error::TypeTiny::Assertion;
  72         189  
  72         2565  
19 72     72   35918 use Error::TypeTiny::WrongNumberOfParameters;
  72         267  
  72         3114  
20 72     72   21577 use Types::Standard ();
  72         293  
  72         2679  
21 72     72   468 use Types::TypeTiny ();
  72         153  
  72         23034  
22              
23             require Exporter::Tiny;
24             our @ISA = 'Exporter::Tiny';
25              
26             our @EXPORT = qw(
27             compile compile_named
28             );
29              
30             our @EXPORT_OK = qw(
31             compile_named_oo
32             validate validate_named
33             multisig
34             Invocant ArgsObject
35             wrap_subs wrap_methods
36             signature signature_for signature_for_func signature_for_method
37             );
38              
39             our %EXPORT_TAGS = (
40             compile => [ qw( compile compile_named compile_named_oo ) ],
41             wrap => [ qw( wrap_subs wrap_methods ) ],
42             sigs => [ qw( signature signature_for ) ],
43             validate => [ qw( validate validate_named ) ],
44             sigplus => [ qw( signature signature_for signature_for_func signature_for_method ) ],
45            
46             v1 => [ qw( compile compile_named ) ], # Old default
47             v2 => [ qw( signature signature_for ) ], # New recommendation
48             );
49              
50             BEGIN {
51 72     72   374 my $pfx = $ENV{'PERL_TYPE_PARAMS_SUBNAME_PREFIX'};
52 72 100 100     7226 eval sprintf(
    100          
53             'sub SIGNATURE_SUBNAME_PREFIX () { %s }',
54             B::perlstring(
55             ( defined $pfx and $pfx =~ /::\z/ ) ? $pfx : $pfx ? 'SIGNATURE_FOR::' : '',
56             )
57             );
58              
59 72         522 my $sfx = $ENV{'PERL_TYPE_PARAMS_SUBNAME_SUFFIX'};
60 72 100 100     86634 eval sprintf(
    100          
61             'sub SIGNATURE_SUBNAME_SUFFIX () { %s }',
62             B::perlstring(
63             ( defined $sfx and $sfx =~ /\A_/ ) ? $sfx : $sfx ? '_SIGNATURE' : '',
64             )
65             );
66             };
67              
68             {
69             my $Invocant;
70            
71             sub Invocant () {
72 1   33 1 1 842 $Invocant ||= do {
73 1         577 require Type::Tiny::Union;
74 1         6 'Type::Tiny::Union'->new(
75             name => 'Invocant',
76             type_constraints => [
77             Types::Standard::Object(),
78             Types::Standard::ClassName(),
79             ],
80             );
81             };
82             } #/ sub Invocant
83            
84             my $ArgsObject;
85            
86             sub ArgsObject (;@) {
87 11   66 11 1 3417 $ArgsObject ||= do {
88             'Type::Tiny'->new(
89             name => 'ArgsObject',
90             parent => Types::Standard::Object(),
91             constraint => q{ ref($_) =~ qr/^Type::Params::OO::/ },
92             constraint_generator => sub {
93 7     7   42 Type::Tiny::check_parameter_count_for_parameterized_type( 'Type::Params', 'ArgsObject', \@_, 1, 1 );
94 7         34 my $param = Types::Standard::assert_Str( shift );
95 7 50       108 sub { defined( $_->{'~~caller'} ) and $_->{'~~caller'} eq $param };
  4         53  
96             },
97             inline_generator => sub {
98 7     7   16 my $param = shift;
99 7         27 my $quoted = B::perlstring( $param );
100             sub {
101 15         34 my $var = pop;
102             return (
103 15         57 Types::Standard::Object()->inline_check( $var ),
104             sprintf( q{ ref(%s) =~ qr/^Type::Params::OO::/ }, $var ),
105             sprintf(
106             q{ do { use Scalar::Util (); Scalar::Util::reftype(%s) eq 'HASH' } }, $var
107             ),
108             sprintf(
109             q{ defined((%s)->{'~~caller'}) && ((%s)->{'~~caller'} eq %s) }, $var, $var,
110             $quoted
111             ),
112             );
113 7         50 };
114             },
115 3         22 );
116             };
117            
118 11 100       60 @_ ? $ArgsObject->parameterize( @{ $_[0] } ) : $ArgsObject;
  4         27  
119             } #/ sub ArgsObject (;@)
120            
121             &Scalar::Util::set_prototype( \&ArgsObject, ';$' )
122             if Eval::TypeTiny::NICE_PROTOTYPES;
123             }
124              
125             sub signature {
126 260 100   260 1 1596597 if ( @_ % 2 ) {
127 1         11 require Error::TypeTiny;
128 1         7 Error::TypeTiny::croak( "Expected even-sized list of arguments" );
129             }
130 259         1271 my ( %opts ) = @_;
131 259 100 33     1123 $opts{next} ||= delete $opts{goto_next} if exists $opts{goto_next};
132              
133 259   66     3617 my $for = [ caller( 1 + ( $opts{caller_level} || 0 ) ) ]->[3] || ( ( $opts{package} || '__ANON__' ) . '::__ANON__' );
134 259         2922 my ( $pkg, $sub ) = ( $for =~ /^(.+)::(\w+)$/ );
135 259   66     1938 $opts{package} ||= $pkg;
136 259   66     1934 $opts{subname} ||= $sub;
137              
138 259         43233 require Type::Params::Signature;
139 259         2680 'Type::Params::Signature'->new_from_v2api( \%opts )->return_wanted;
140             }
141              
142             sub signature_for {
143 48 100   48 1 3951632 if ( not @_ % 2 ) {
144 1         11 require Error::TypeTiny;
145 1         5 Error::TypeTiny::croak( "Expected odd-sized list of arguments; did you forget the function name?" );
146             }
147 47         266 my ( $function, %opts ) = @_;
148 47   66     532 my $package = $opts{package} || caller( $opts{caller_level} || 0 );
149 47 100 33     195 $opts{next} ||= delete $opts{goto_next} if exists $opts{goto_next};
150              
151 47 100       212 if ( ref($function) eq 'ARRAY' ) {
152 3         9 $opts{package} = $package;
153 3         11 return map { signature_for( $_, %opts ) } @$function;
  6         31  
154             }
155            
156 44         159 $opts{_is_signature_for} = 1;
157              
158 44 50       319 my $fullname = ( $function =~ /::/ ) ? $function : "$package\::$function";
159 44   66     295 $opts{package} ||= $package;
160 44 50 33     407 $opts{subname} ||= ( $function =~ /::(\w+)$/ ) ? $1 : $function;
161 72 100 100 72   674 $opts{next} ||= do { no strict 'refs'; exists(&$fullname) ? \&$fullname : undef; };
  72         153  
  72         15688  
  44         248  
  38         316  
162 44 100       179 if ( $opts{method} ) {
163 12   66     47 $opts{next} ||= eval { $package->can( $opts{subname} ) };
  1         16  
164             }
165 44 100 100     244 if ( $opts{fallback} and not $opts{next} ) {
166 1 50   0   7 $opts{next} = ref( $opts{fallback} ) ? $opts{fallback} : sub {};
167             }
168 44 100       181 if ( not $opts{next} ) {
169 1         8 require Error::TypeTiny;
170 1         8 return Error::TypeTiny::croak( "Function '$function' not found to wrap!" );
171             }
172              
173 43         15460 require Type::Params::Signature;
174 43         574 my $sig = 'Type::Params::Signature'->new_from_v2api( \%opts );
175             # Delay compilation
176 41         102 my $compiled;
177             my $coderef = sub {
178 40   33 40   1848644 $compiled ||= $sig->coderef->compile;
        16      
        4      
179            
180 72     72   534 no strict 'refs';
  72         150  
  72         3072  
181 72     72   3867 no warnings 'redefine';
  72         156  
  72         11963  
182 40         279 *$fullname = set_subname( SIGNATURE_SUBNAME_PREFIX . $fullname . SIGNATURE_SUBNAME_SUFFIX, $compiled );
183            
184 40         1163 goto( $compiled );
185 41         346 };
186              
187 41         140 our ( %PRE_INSTALL, %POST_INSTALL );
188 41 100       210 if ( my $cb = $PRE_INSTALL{$package} ) {
189 1         7 Types::Standard::assert_ArrayRef( $cb );
190 1         14 $_->( $sig ) for @$cb;
191             }
192              
193 72     72   494 no strict 'refs';
  72         146  
  72         2691  
194 72     72   445 no warnings 'redefine';
  72         154  
  72         101959  
195 41         331 *$fullname = set_subname( SIGNATURE_SUBNAME_PREFIX . $fullname . SIGNATURE_SUBNAME_SUFFIX, $coderef );
196              
197 41 100       175 if ( my $cb = $POST_INSTALL{$package} ) {
198 1         125 Types::Standard::assert_ArrayRef( $cb );
199 1         12 $_->( $sig ) for @$cb;
200             }
201              
202 41         11100 return $sig;
203             }
204              
205             sub signature_for_func {
206 2 100   2 1 28 if ( not @_ % 2 ) {
207 1         7 require Error::TypeTiny;
208 1         7 Error::TypeTiny::croak( "Expected odd-sized list of arguments; did you forget the function name?" );
209             }
210 1         7 my ( $function, %opts ) = @_;
211 1         4 my $N = !!$opts{named};
212 1         8 @_ = ( $function, method => 0, allow_dash => $N, list_to_named => $N, %opts );
213 1         7 goto \&signature_for;
214             }
215              
216             sub signature_for_method {
217 2 100   2 1 25 if ( not @_ % 2 ) {
218 1         8 require Error::TypeTiny;
219 1         8 Error::TypeTiny::croak( "Expected odd-sized list of arguments; did you forget the function name?" );
220             }
221 1         6 my ( $function, %opts ) = @_;
222 1         5 my $N = !!$opts{named};
223 1         7 @_ = ( $function, method => 1, allow_dash => $N, list_to_named => $N, %opts );
224 1         6 goto \&signature_for;
225             }
226              
227             sub compile {
228 83     83 1 1980550 my @args = @_;
229 83         372 @_ = ( positional => \@args );
230 83         515 goto \&signature;
231             }
232              
233             sub compile_named {
234 85     85 1 1459698 my @args = @_;
235 85         410 @_ = ( bless => 0, named => \@args );
236 85         509 goto \&signature;
237             }
238              
239             sub compile_named_oo {
240 20     20 1 769532 my @args = @_;
241 20         91 @_ = ( bless => 1, named => \@args );
242 20         104 goto \&signature;
243             }
244              
245             # Would be faster to inline this into validate and validate_named, but
246             # that would complicate them. :/
247             sub _mk_key {
248 933     933   1421 local $_;
249             join ':', map {
250 933         1880 Types::Standard::is_HashRef( $_ ) ? do {
251 488         1616 my %h = %$_;
252 488         1418 sprintf( '{%s}', _mk_key( map { ; $_ => $h{$_} } sort keys %h ) );
  610         1943  
253             } :
254 4623 50       127156 Types::TypeTiny::is_TypeTiny( $_ ) ? sprintf( 'TYPE=%s', $_->{uniq} ) :
    100          
    100          
    100          
255             Types::Standard::is_Ref( $_ ) ? sprintf( 'REF=%s', refaddr( $_ ) ) :
256             Types::Standard::is_Undef( $_ ) ? sprintf( 'UNDEF' ) :
257             B::perlstring( $_ )
258             } @_;
259             } #/ sub _mk_key
260              
261             {
262             my %compiled;
263             sub validate {
264 15     15 1 94 my $arg = shift;
265             my $sub = (
266             $compiled{ _mk_key( @_ ) } ||= signature(
267             caller_level => 1,
268 15 50 66     48 %{ ref( $_[0] ) eq 'HASH' ? shift( @_ ) : +{} },
  9         73  
269             positional => [ @_ ],
270             )
271             );
272 15         201 @_ = @$arg;
273 15         61 goto $sub;
274             } #/ sub validate
275             }
276              
277             {
278             my %compiled;
279             sub validate_named {
280 430     430 1 479318 my $arg = shift;
281             my $sub = (
282             $compiled{ _mk_key( @_ ) } ||= signature(
283             caller_level => 1,
284             bless => 0,
285 430 100 66     1513 %{ ref( $_[0] ) eq 'HASH' ? shift( @_ ) : +{} },
  28         337  
286             named => [ @_ ],
287             )
288             );
289 430         2156 @_ = @$arg;
290 430         1772 goto $sub;
291             } #/ sub validate_named
292             }
293              
294             sub multisig {
295 7 100   7 1 80 my %options = ( ref( $_[0] ) eq "HASH" ) ? %{ +shift } : ();
  3         18  
296 7         37 signature(
297             %options,
298             multi => \@_,
299             );
300             } #/ sub multisig
301              
302             sub wrap_methods {
303 2 50   2 1 41 my $opts = ref( $_[0] ) eq 'HASH' ? shift : {};
304 2   33     20 $opts->{caller} ||= caller;
305 2         7 $opts->{skip_invocant} = 1;
306 2         7 $opts->{use_can} = 1;
307 2         9 unshift @_, $opts;
308 2         11 goto \&_wrap_subs;
309             }
310              
311             sub wrap_subs {
312 1 50   1 1 6 my $opts = ref( $_[0] ) eq 'HASH' ? shift : {};
313 1   33     9 $opts->{caller} ||= caller;
314 1         3 $opts->{skip_invocant} = 0;
315 1         3 $opts->{use_can} = 0;
316 1         3 unshift @_, $opts;
317 1         4 goto \&_wrap_subs;
318             }
319              
320             sub _wrap_subs {
321 3     3   7 my $opts = shift;
322 3         11 while ( @_ ) {
323 8         36 my ( $name, $proto ) = splice @_, 0, 2;
324 8 50       48 my $fullname = ( $name =~ /::/ ) ? $name : sprintf( '%s::%s', $opts->{caller}, $name );
325 8         16 my $orig = do {
326 72     72   672 no strict 'refs';
  72         234  
  72         18980  
327             exists &$fullname ? \&$fullname
328       2     : $opts->{use_can} ? ( $opts->{caller}->can( $name ) || sub { } )
329       1     : sub { }
330 8 100 66     86 };
    100          
331 8         16 my $new;
332 8 100       21 if ( ref $proto eq 'CODE' ) {
333             $new = $opts->{skip_invocant}
334             ? sub {
335 4     4   24 my $s = shift;
336 4         39 @_ = ( $s, &$proto );
337 2         648 goto $orig;
338             }
339             : sub {
340 2     2   14 @_ = &$proto;
341 1         45 goto $orig;
342 2 100       14 };
343             }
344             else {
345             $new = compile(
346             {
347             'package' => $opts->{caller},
348             'subname' => $name,
349             'next' => $orig,
350 6 100       58 'head' => $opts->{skip_invocant} ? 1 : 0,
351             },
352             @$proto,
353             );
354             }
355 72     72   582 no strict 'refs';
  72         602  
  72         6380  
356 72     72   446 no warnings 'redefine';
  72         259  
  72         10580  
357 8         142 *$fullname = set_subname( $fullname, $new );
358             } #/ while ( @_ )
359 3         14 1;
360             } #/ sub _wrap_subs
361              
362             1;
363              
364             __END__