File Coverage

blib/lib/Type/Params/Parameter.pm
Criterion Covered Total %
statement 196 200 98.0
branch 104 110 94.5
condition 64 77 83.1
subroutine 30 31 96.7
pod 18 19 94.7
total 412 437 94.2


line stmt bran cond sub pod time code
1             package Type::Params::Parameter;
2              
3 64     64   1689 use 5.008001;
  64         283  
4 64     64   1913 use strict;
  64         163  
  64         2073  
5 64     64   386 use warnings;
  64         139  
  64         6244  
6              
7             BEGIN {
8 64 50   64   3160 if ( $] < 5.010 ) { require Devel::TypeTiny::Perl58Compat }
  0         0  
9             }
10              
11             BEGIN {
12 64     64   242 $Type::Params::Parameter::AUTHORITY = 'cpan:TOBYINK';
13 64         3386 $Type::Params::Parameter::VERSION = '2.010001';
14             }
15              
16             $Type::Params::Parameter::VERSION =~ tr/_//d;
17              
18 64     64   478 use Types::Standard qw( -is -types );
  64         183  
  64         736  
19              
20             my $RE_WORDLIKE = qr/\A[^\W0-9]\w*\z/;
21              
22             my $Attrs = Enum[ qw/
23             name type slurpy default alias strictness coerce clone in_list optional
24             getter predicate allow_dash vartail default_on_undef
25             quux
26             / ];
27              
28             sub _croak {
29 1     1   11 require Carp;
30 1         261 Carp::croak( pop );
31             }
32              
33             sub new {
34 796     796 1 1535 my $class = shift;
35              
36 796 50       3838 my %self = @_ == 1 ? %{$_[0]} : @_;
  0         0  
37 796   100     6721 $self{alias} ||= [];
38 796 100 66     4007 if ( defined $self{alias} and not ref $self{alias} ) {
39 13         88 $self{alias} = [ $self{alias} ];
40             }
41              
42 796         3086 my $self = bless \%self, $class;
43            
44 796 100       9632 $Attrs->all( sort keys %$self ) or do {
45 1         4 require Carp;
46 1         4 require Type::Utils;
47 1         3 my @bad = ( ~ $Attrs )->grep( sort keys %$self );
48 1 50       3 Carp::carp( sprintf(
49             "Warning: unrecognized parameter %s: %s, continuing anyway",
50             @bad == 1 ? 'option' : 'options',
51             Type::Utils::english_list( @bad ),
52             ) );
53             };
54              
55 796         3434 return $self;
56             }
57              
58 3     3 1 23 sub name { $_[0]{name} } sub has_name { exists $_[0]{name} }
  2288     2288 1 18615  
59 3     3 1 19 sub type { $_[0]{type} } sub has_type { exists $_[0]{type} }
  3398     3398 1 12302  
60 22     22 1 58 sub default { $_[0]{default} } sub has_default { exists $_[0]{default} }
  1800     1800 1 9227  
61 593     593 1 1661 sub alias { $_[0]{alias} } sub has_alias { @{ $_[0]{alias} } }
  0     0 1 0  
  0         0  
62 780     780 1 3671 sub strictness { $_[0]{strictness} } sub has_strictness { exists $_[0]{strictness} }
  1     1 1 4  
63              
64 963     963 0 3038 sub should_clone { $_[0]{clone} }
65 22     22 1 85 sub default_on_undef { $_[0]{default_on_undef} }
66              
67             sub in_list {
68 20 100   20 1 90 return $_[0]{in_list} if exists $_[0]{in_list};
69 9         24 $_[0]{in_list} = !$_[0]->optional;
70             }
71              
72             sub coerce {
73             exists( $_[0]{coerce} )
74             ? $_[0]{coerce}
75 168 50   168 1 743 : ( $_[0]{coerce} = $_[0]->type->has_coercion )
76             }
77              
78             sub optional {
79             exists( $_[0]{optional} )
80             ? $_[0]{optional}
81 2315 100   2315 1 7924 : do {
82             $_[0]{optional} = $_[0]->has_default || grep(
83             $_->{uniq} == Optional->{uniq},
84 769   100     2342 $_[0]->type, $_[0]->type->parents,
85             );
86             }
87             }
88              
89             sub getter {
90             exists( $_[0]{getter} )
91             ? $_[0]{getter}
92             : ( $_[0]{getter} = $_[0]{name} )
93 196 100   196 1 891 }
94              
95             sub predicate {
96             exists( $_[0]{predicate} )
97             ? $_[0]{predicate}
98 196 100   196 1 860 : ( $_[0]{predicate} = ( $_[0]->optional ? 'has_' . $_[0]{name} : undef ) )
    100          
99             }
100              
101             sub might_supply_new_value {
102 181 100 100 181 1 640 $_[0]->has_default or $_[0]->coerce or $_[0]->should_clone;
103             }
104              
105             sub _all_aliases {
106 593     593   1409 my ( $self, $signature ) = @_;
107 593         1144 my $allow_dash = $self->{allow_dash};
108 593 100       2265 $allow_dash = $signature->allow_dash if !defined $allow_dash;
109 593         952 my @aliases;
110 593 100 66     1576 if ( $allow_dash and $self->name =~ $RE_WORDLIKE ) {
111 23         91 push @aliases, sprintf( '-%s', $self->name );
112             }
113 593         1013 for my $name ( @{ $self->alias } ) {
  593         1583  
114 32         66 push @aliases, $name;
115 32 100 66     135 if ( $allow_dash and $name =~ $RE_WORDLIKE ) {
116 3         9 push @aliases, sprintf( '-%s', $name );
117             }
118             }
119 593         2414 return @aliases;
120             }
121              
122             sub _code_for_default {
123 22     22   79 my ( $self, $signature, $coderef ) = @_;
124 22         79 my $default = $self->default;
125              
126 22 100       106 if ( is_CodeRef $default ) {
127             my $default_varname = $coderef->add_variable(
128             '$default_for_' . $self->{vartail},
129 5         65 \$default,
130             );
131 5         26 return sprintf( '%s->( %s )', $default_varname, $signature->method_invocant );
132             }
133 17 100       75 if ( is_Undef $default ) {
134 2         15 return 'undef';
135             }
136 15 100       53 if ( is_Str $default ) {
137 9         87 return B::perlstring( $default );
138             }
139 6 100       28 if ( is_HashRef $default ) {
140 2         14 return '{}';
141             }
142 4 100       17 if ( is_ArrayRef $default ) {
143 2         17 return '[]';
144             }
145 2 100       12 if ( is_ScalarRef $default ) {
146 1         8 return $$default;
147             }
148              
149 1         6 $self->_croak( 'Default expected to be undef, string, coderef, or empty arrayref/hashref' );
150             }
151              
152             sub _maybe_clone {
153 24     24   74 my ( $self, $varname ) = @_;
154              
155 24 100       81 if ( $self->should_clone ) {
156 2         8 return sprintf( 'Storable::dclone( %s )', $varname );
157             }
158 22         106 return $varname;
159             }
160              
161             sub _make_code {
162 780     780   6131 my ( $self, %args ) = ( shift, @_ );
163              
164 780   100     3175 my $type = $args{type} || 'arg';
165 780         1714 my $signature = $args{signature};
166 780         1491 my $coderef = $args{coderef};
167 780         1597 my $varname = $args{input_slot};
168 780         1554 my $index = $args{index};
169 780         2259 my $constraint = $self->type;
170 780         2712 my $is_optional = $self->optional;
171             my $really_optional =
172             $is_optional
173             && $constraint->parent
174             && $constraint->parent->{uniq} eq Optional->{uniq}
175 780   66     7519 && $constraint->type_parameter;
176            
177             # Allow Optional itself, without any parameter.
178             $really_optional = Types::Standard::Any
179 780 100 66     8318 if $constraint && $constraint->{uniq} eq Optional->{uniq};
180              
181 780         10327 my $strictness;
182 780 100       2456 if ( $self->has_strictness ) {
    100          
183 1         6 $strictness = \ $self->strictness;
184             }
185             elsif ( $signature->has_strictness ) {
186 9         25 $strictness = \ $signature->strictness;
187             }
188              
189 780         1760 my ( $vartail, $exists_check );
190 780 100       2099 if ( $args{is_named} ) {
191 391         755 my $bit = $args{key};
192 391 50       1303 $bit =~ s/([_\W])/$1 eq '_' ? '__' : sprintf('_%x', ord($1))/ge;
  2         26  
193 391         857 $vartail = $type . '_' . $bit;
194 391         1030 $exists_check = sprintf 'exists( %s )', $args{input_slot};
195             }
196             else {
197 389   100     2691 ( my $input_count_varname = $args{input_var} || '' ) =~ s/\@/\$\#/;
198 389         1177 $vartail = $type . '_' . $index;
199 389         1464 $exists_check = sprintf '%s >= %d', $input_count_varname, $index;
200             }
201              
202 780         1498 my $block_needs_ending = 0;
203 780         2256 my $needs_clone = $self->should_clone;
204 780         1457 my $in_big_optional_block = 0;
205              
206 780 100 66     2382 if ( $needs_clone and not $signature->{loaded_Storable} ) {
207 2         5 $coderef->add_line( 'use Storable ();' );
208 2         5 $coderef->add_gap;
209 2         4 $signature->{loaded_Storable} = 1;
210             }
211              
212             $coderef->add_line( sprintf(
213             '# Parameter %s (type: %s)',
214             $self->name || $args{input_slot},
215 780   66     3946 $constraint->display_name,
216             ) );
217              
218 780 100 100     3549 if ( $args{is_named} and my @aliases = $self->_all_aliases($signature) ) {
219 22         191 $coderef->add_line( sprintf(
220             'for my $alias ( %s ) {',
221             join( q{, }, map B::perlstring($_), @aliases ),
222             ) );
223 22         90 $coderef->increase_indent;
224 22         70 $coderef->add_line( 'exists $in{$alias} or next;' );
225 22         85 $coderef->add_line( sprintf(
226             'if ( %s ) {',
227             $exists_check,
228             ) );
229 22         70 $coderef->increase_indent;
230             $coderef->add_line( sprintf(
231             '%s;',
232             $signature->_make_general_fail(
233             coderef => $coderef,
234 22   33     84 message => q{sprintf( 'Superfluous alias "%s" for argument "%s"', $alias, } . B::perlstring( $self->name || $args{input_slot} ) . q{ )},
235             ),
236             ) );
237 22         369 $coderef->decrease_indent;
238 22         68 $coderef->add_line( '}' );
239 22         98 $coderef->add_line( 'else {' );
240 22         70 $coderef->increase_indent;
241 22         88 $coderef->add_line( sprintf(
242             '%s = delete( $in{$alias} );',
243             $varname,
244             ) );
245 22         64 $coderef->decrease_indent;
246 22         78 $coderef->add_line( '}' );
247 22         69 $coderef->decrease_indent;
248 22         205 $coderef->add_line( '}' );
249             }
250              
251 780 100 100     2928 if ( $args{is_named} and $signature->list_to_named and $self->in_list ) {
      100        
252 9         59 $coderef->addf( 'if ( not exists %s ) {', $varname );
253 9         31 $coderef->increase_indent;
254 9         27 $coderef->addf( 'for my $ix ( 0 .. $#positional ) {' );
255 9         51 $coderef->increase_indent;
256 9   33     63 $coderef->addf( '%s or next;', ( $really_optional or $constraint )->coercibles->inline_check( '$positional[$ix]' ) );
257 9         39 $coderef->addf( '( %s ) = splice( @positional, $ix, 1 );', $varname );
258 9         30 $coderef->addf( 'last;' );
259 9         35 $coderef->decrease_indent;
260 9         30 $coderef->addf( '}' );
261 9         33 $coderef->decrease_indent;
262 9         26 $coderef->addf( '}' );
263             }
264              
265 780 100       2167 if ( $self->has_default ) {
    100          
    100          
266 22         45 my $check = $exists_check;
267 22 100       72 if ( $self->default_on_undef ) {
268 2         9 $check = "( $check and defined $varname )";
269             }
270 22         110 $self->{vartail} = $vartail; # hack
271 22         93 $coderef->add_line( sprintf(
272             '$dtmp = %s ? %s : %s;',
273             $check,
274             $self->_maybe_clone( $varname ),
275             $self->_code_for_default( $signature, $coderef ),
276             ) );
277 21         48 $varname = '$dtmp';
278 21         75 $needs_clone = 0;
279             }
280             elsif ( $self->optional ) {
281 108 100       333 if ( $args{is_named} ) {
282 89         416 $coderef->add_line( sprintf(
283             'if ( %s ) {',
284             $exists_check,
285             ) );
286 89         277 $coderef->{indent} .= "\t";
287 89         365 ++$block_needs_ending;
288 89         179 ++$in_big_optional_block;
289             }
290             else {
291 19         85 $coderef->add_line( sprintf(
292             "%s\n\tor %s;",
293             $exists_check,
294             $signature->_make_return_expression( is_early => 1 ),
295             ) );
296             }
297             }
298             elsif ( $args{is_named} ) {
299 294         1517 $coderef->add_line( sprintf(
300             "%s\n\tor %s;",
301             $exists_check,
302             $signature->_make_general_fail(
303             coderef => $coderef,
304             message => "'Missing required parameter: $args{key}'",
305             ),
306             ) );
307             }
308              
309 779 100       2250 if ( $needs_clone ) {
310 2         6 $coderef->add_line( sprintf(
311             '$dtmp = %s;',
312             $self->_maybe_clone( $varname ),
313             ) );
314 2         2 $varname = '$dtmp';
315 2         3 $needs_clone = 0;
316             }
317              
318 779 100 100     3000 if ( $constraint->has_coercion and $constraint->coercion->can_be_inlined ) {
    100          
319 84 100       369 $coderef->add_line( sprintf(
320             '$tmp%s = %s;',
321             ( $is_optional ? '{x}' : '' ),
322             $constraint->coercion->inline_coercion( $varname )
323             ) );
324 84 100       304 $varname = '$tmp' . ( $is_optional ? '{x}' : '' );
325             }
326             elsif ( $constraint->has_coercion ) {
327 83         332 my $coercion_varname = $coderef->add_variable(
328             '$coercion_for_' . $vartail,
329             \ $constraint->coercion->compiled_coercion,
330             );
331 83 100       623 $coderef->add_line( sprintf(
332             '$tmp%s = &%s( %s );',
333             ( $is_optional ? '{x}' : '' ),
334             $coercion_varname,
335             $varname,
336             ) );
337 83 100       290 $varname = '$tmp' . ( $is_optional ? '{x}' : '' );
338             }
339              
340 779         3664 undef $Type::Tiny::ALL_TYPES{ $constraint->{uniq} };
341 779         2077 $Type::Tiny::ALL_TYPES{ $constraint->{uniq} } = $constraint;
342              
343 779         1635 my $strictness_test = '';
344 779 100 100     4136 if ( $strictness and $$strictness eq 1 ) {
    100 100        
345 3         7 $strictness_test = '';
346             }
347             elsif ( $strictness and $$strictness ) {
348 5         14 $strictness_test = sprintf "( not %s )\n\tor ", $$strictness;
349             }
350              
351 779 100 100     5682 if ( $strictness and not $$strictness ) {
    100 100        
    100          
    100          
352 2         7 $coderef->add_line( '1; # ... nothing to do' );
353             }
354             elsif ( $constraint->{uniq} == Any->{uniq} ) {
355 13         99 $coderef->add_line( '1; # ... nothing to do' );
356             }
357             elsif ( $args{is_slurpy} and $self->_dont_validate_slurpy ) {
358 3         17 $coderef->add_line( '1; # ... nothing to do' );
359             }
360             elsif ( $constraint->can_be_inlined ) {
361             $coderef->add_line( $strictness_test . sprintf(
362             "%s\n\tor %s;",
363             ( $really_optional or $constraint )->inline_check( $varname ),
364             $signature->_make_constraint_fail(
365             coderef => $coderef,
366             parameter => $self,
367             constraint => $constraint,
368             varname => $varname,
369             display_var => $args{display_var},
370 700   66     3513 ),
371             ) );
372             }
373             else {
374 61   66     355 my $compiled_check_varname = $coderef->add_variable(
375             '$check_for_' . $vartail,
376             \ ( ( $really_optional or $constraint )->compiled_check ),
377             );
378             $coderef->add_line( $strictness_test . sprintf(
379             "&%s( %s )\n\tor %s;",
380             $compiled_check_varname,
381             $varname,
382             $signature->_make_constraint_fail(
383             coderef => $coderef,
384             parameter => $self,
385             constraint => $constraint,
386             varname => $varname,
387             display_var => $args{display_var},
388 61         359 ),
389             ) );
390             }
391              
392 779 100 100     5014 if ( $args{output_var} ) {
    100          
393             $coderef->add_line( sprintf(
394             'push( %s, %s );',
395             $args{output_var},
396 110         536 $varname,
397             ) );
398             }
399             elsif ( $args{output_slot} and $args{output_slot} ne $varname ) {
400 431 100 100     2093 if ( !$in_big_optional_block and $varname =~ /\{/ ) {
401             $coderef->add_line( sprintf(
402             '%s = %s if exists( %s );',
403             $args{output_slot},
404 234         1159 $varname,
405             $varname,
406             ) );
407             }
408             else {
409             $coderef->add_line( sprintf(
410             '%s = %s;',
411             $args{output_slot},
412 197         754 $varname,
413             ) );
414             }
415             }
416              
417 779 100       3004 if ( $args{is_named} ) {
418             $coderef->add_line( sprintf(
419             'delete( %s );',
420             $args{input_slot},
421 391         1233 ) );
422             }
423              
424 779 100       2180 if ( $block_needs_ending ) {
425 89         567 $coderef->{indent} =~ s/\s$//;
426 89         294 $coderef->add_line( '}' );
427             }
428              
429 779         2922 $coderef->add_gap;
430              
431 779         4831 $self;
432             }
433              
434             # This list can be reused safely.
435             my @uniqs;
436              
437             # If $SLURPY is one of a handful of very loose type constraints, there is
438             # no need to validate it because we built it as a hashref or arrayref ourself,
439             # so there's no way it couldn't be a hashref or arrayref.
440             sub _dont_validate_slurpy {
441 70     70   731 my $self = shift;
442 70 50       255 my $type = $self->type or return 1;
443 70 100       276 if ( not @uniqs ) {
444 20         95 @uniqs = map { $_->{uniq} }
  220         825  
445             Slurpy,
446             Slurpy[Any], Any,
447             Slurpy[Item], Item,
448             Slurpy[Ref], Ref,
449             Slurpy[HashRef], HashRef,
450             Slurpy[ArrayRef], ArrayRef;
451             }
452 70   100     809 ( $_ == $type->{uniq} and return 1 ) for @uniqs;
453 67         520 return 0;
454             }
455              
456             1;
457              
458             __END__