File Coverage

blib/lib/Specio/Constraint/Role/Interface.pm
Criterion Covered Total %
statement 229 244 93.8
branch 39 48 81.2
condition 15 21 71.4
subroutine 63 74 85.1
pod 0 28 0.0
total 346 415 83.3


line stmt bran cond sub pod time code
1              
2             use strict;
3 30     30   5110 use warnings;
  30         53  
  30         744  
4 30     30   130  
  30         52  
  30         1061  
5             our $VERSION = '0.48';
6              
7             use Carp qw( confess );
8 30     30   140 use Eval::Closure qw( eval_closure );
  30         50  
  30         1227  
9 30     30   6358 use List::Util 1.33 qw( all any first );
  30         18462  
  30         1327  
10 30     30   163 use Specio::Exception;
  30         672  
  30         1552  
11 30     30   9490 use Specio::PartialDump qw( partial_dump );
  30         66  
  30         909  
12 30     30   179 use Specio::TypeChecks qw( is_CodeRef );
  30         53  
  30         1244  
13 30     30   148  
  30         52  
  30         1303  
14             use Role::Tiny 1.003003;
15 30     30   145  
  30         596  
  30         162  
16             use Specio::Role::Inlinable;
17 30     30   10893 with 'Specio::Role::Inlinable';
  30         60  
  30         1542  
18              
19             use overload(
20             q{""} => '_stringify',
21             '&{}' => '_subification',
22             'bool' => sub {1},
23 4030     4030   12430 'eq' => 'is_same_type_as',
24 30         206 );
25 30     30   171  
  30         50  
26             {
27             ## no critic (Subroutines::ProtectPrivateSubs)
28             my $role_attrs = Specio::Role::Inlinable::_attrs();
29             ## use critic
30              
31             my $attrs = {
32             %{$role_attrs},
33             name => {
34             isa => 'Str',
35             predicate => '_has_name',
36             },
37             parent => {
38             does => 'Specio::Constraint::Role::Interface',
39             predicate => '_has_parent',
40             },
41             _constraint => {
42             isa => 'CodeRef',
43             init_arg => 'constraint',
44             predicate => '_has_constraint',
45             },
46             _optimized_constraint => {
47             isa => 'CodeRef',
48             init_arg => undef,
49             lazy => 1,
50             builder => '_build_optimized_constraint',
51             },
52             _ancestors => {
53             isa => 'ArrayRef',
54             init_arg => undef,
55             lazy => 1,
56             builder => '_build_ancestors',
57             },
58             _message_generator => {
59             isa => 'CodeRef',
60             init_arg => undef,
61             },
62             _coercions => {
63             builder => '_build_coercions',
64             clone => '_clone_coercions',
65             },
66             _subification => {
67             init_arg => undef,
68             lazy => 1,
69             builder => '_build_subification',
70             },
71              
72             # Because types are cloned on import, we can't directly compare type
73             # objects. Because type names can be reused between packages (no global
74             # registry) we can't compare types based on name either.
75             _signature => {
76             isa => 'Str',
77             init_arg => undef,
78             lazy => 1,
79             builder => '_build_signature',
80             },
81             };
82              
83             ## no critic (Subroutines::ProhibitUnusedPrivateSubroutines)
84             return $attrs;
85             }
86 9060     9060   17962 }
87              
88             my $NullConstraint = sub {1};
89              
90             # See Specio::OO to see how this is used.
91              
92             ## no critic (Subroutines::ProhibitUnusedPrivateSubroutines)
93             my $self = shift;
94             my $p = shift;
95              
96 927     927   12568 unless ( $self->_has_constraint || $self->_has_inline_generator ) {
97 927         1178 $self->{_constraint} = $NullConstraint;
98             }
99 927 100 100     2027  
100 26         213 die
101             'A type constraint should have either a constraint or inline_generator parameter, not both'
102             if $self->_has_constraint && $self->_has_inline_generator;
103              
104 927 50 66     6835 $self->{_message_generator}
105             = $self->_wrap_message_generator( $p->{message_generator} );
106              
107             return;
108 927         4788 }
109             ## use critic
110 927         2173  
111             my $self = shift;
112             my $generator = shift;
113              
114             unless ( defined $generator ) {
115 902     902   1224 $generator = sub {
116 902         1725 my $description = shift;
117             my $value = shift;
118 902 50       1634  
119             return "Validation failed for $description with value "
120 2359     2359   4194 . partial_dump($value);
121 2359         3462 };
122             }
123 2359         9216  
124             my $d = $self->description;
125 902         3968  
126             return sub { $generator->( $d, @_ ) };
127             }
128 902         2261  
129              
130 902     2359   5664 my $self = shift;
  2359         143864  
131             my $value = shift;
132              
133 44     44 0 57 return if $self->value_is_valid($value);
  44         206  
134 0     0 0 0  
135 22     22   84 Specio::Exception->throw(
136 18     18   45 message => $self->_message_generator->($value),
137 63     63 0 3592 type => $self,
  63         445  
138             value => $value,
139             );
140 308     308 0 3564 }
141 308         637  
142             my $self = shift;
143 308 100       1104 my $value = shift;
144              
145 261         9757 return $self->_optimized_constraint->($value);
146             }
147              
148             my $self = shift;
149              
150             return ( ( reverse @{ $self->_ancestors } ), $self );
151             }
152              
153 3329     3329 0 26889 my $self = shift;
154 3329         4891 my $type = shift;
155              
156 3329         11120 return
157             any { $_->_signature eq $type->_signature }
158             $self->_ancestors_and_self;
159             }
160 919     919   1198  
161             my $self = shift;
162 919         1111 my $type = shift;
  919         1986  
163              
164             return $self->_signature eq $type->_signature;
165             }
166 13     13 0 18  
167 13         14 my $self = shift;
168              
169             return !$self->_has_name;
170 40     40   378 }
171 13         58  
172             my $self = shift;
173              
174             return ( $self->_has_constraint && $self->_constraint ne $NullConstraint )
175 16     16 0 9325 || $self->_has_inline_generator;
176 16         27 }
177              
178 16         60 my $self = shift;
179              
180             return 1 if $self->_has_inline_generator;
181             return 0
182 996     996 0 14727 if $self->_has_constraint && $self->_constraint ne $NullConstraint;
183              
184 996         1938 # If this type is an empty subtype of an inlinable parent, then we can
185             # inline this type as well.
186             return 1 if $self->_has_parent && $self->parent->can_be_inlined;
187             return 0;
188 405     405 0 603 }
189              
190 405   100     1010 my $self = shift;
191              
192             my $type = $self->_self_or_first_inlinable_ancestor;
193              
194             my $source
195 1262     1262 0 2042 = 'sub { ' . $type->_inline_generator->( $type, '$_[0]' ) . '}';
196              
197 1262 100       2488 return eval_closure(
198 81 100 66     430 source => $source,
199             environment => $type->inline_environment,
200             description => 'inlined sub for ' . $self->description,
201             );
202             }
203 45 50 33     429  
204 0         0 my $self = shift;
205              
206             my $type = first { $_->_has_inline_generator }
207             reverse $self->_ancestors_and_self;
208 350     350   2006  
209             # This should never happen because ->can_be_inlined should always be
210 350         667 # checked before this builder is called.
211             die 'Cannot generate an inline sub' unless $type;
212 350         952  
213             return $type;
214             }
215 350         1183  
216             my $self = shift;
217              
218             if ( $self->can_be_inlined ) {
219             return $self->_generated_inline_sub;
220             }
221             else {
222             return $self->_constraint_with_parents;
223 818     818   1190 }
224             }
225 843     843   5105  
226 818         2518 my $self = shift;
227              
228             my @constraints;
229             for my $type ( $self->_ancestors_and_self ) {
230 818 50       3981 next unless $type->has_real_constraint;
231              
232 818         1326 # If a type can be inlined, we can use that and discard all of the
233             # ancestors we've seen so far, since we can assume that the inlined
234             # constraint does all of the ancestor checks in addition to its own.
235             if ( $type->can_be_inlined ) {
236 109     109   810 @constraints = $type->_generated_inline_sub;
237             }
238 109 100       279 else {
239 99         741 push @constraints, $type->_constraint;
240             }
241             }
242 10         170  
243             return $NullConstraint unless @constraints;
244              
245             return sub {
246             all { $_->( $_[0] ) } @constraints;
247 88     88   217 };
248             }
249 88         151  
250 88         296 # This is only used for identifying from types as part of coercions, but I
251 405 100       58645 # want to leave open the possibility of using something other than
252             # _description in the future.
253             my $self = shift;
254              
255             return $self->description;
256 397 100       4100 }
257 383         2048  
258             my $self = shift;
259             my $coercion = shift;
260 14         135  
261             my $from_id = $coercion->from->id;
262              
263             confess "Cannot add two coercions fom the same type: $from_id"
264 88 50       27643 if $self->_has_coercion_from_type($from_id);
265              
266             $self->_add_coercion( $from_id => $coercion );
267 1267     1267   8074  
  1669         7786  
268 88         800 return;
269             }
270              
271             my $self = shift;
272             my $type = shift;
273              
274             return $self->_has_coercion_from_type( $type->id );
275 22     22 0 63 }
276              
277 22         48 my $self = shift;
278             my $value = shift;
279              
280             for my $coercion ( $self->coercions ) {
281 18     18 0 79 next unless $coercion->from->value_is_valid($value);
282 18         27  
283             return $coercion->coerce($value);
284 18         38 }
285              
286 18 50       116 return $value;
287             }
288              
289 18         40 my $self = shift;
290              
291 18         42 return all { $_->can_be_inlined } $self->coercions;
292             }
293              
294             my $self = shift;
295 4     4 0 6  
296 4         7 return all { $_->can_be_inlined } $self, $self->coercions;
297             }
298 4         7  
299             my $self = shift;
300             my $arg_name = shift;
301              
302 16     16 0 2047 die 'Cannot inline coercion'
303 16         19 unless $self->can_inline_coercion;
304              
305 16         35 my $source = 'do { my $value = ' . $arg_name . ';';
306 22 100       428  
307             my ( $coerce, $env );
308 11         1629 ( $coerce, $arg_name, $env ) = $self->_inline_coercion($arg_name);
309             $source .= $coerce . $arg_name . '};';
310              
311 5         79 return ( $source, $env );
312             }
313              
314             my $self = shift;
315 5     5 0 10 my $arg_name = shift;
316              
317 5     9   60 die 'Cannot inline coercion and check'
  9         35  
318             unless $self->can_inline_coercion_and_check;
319              
320             my $source = 'do { my $value = ' . $arg_name . ';';
321 11     11 0 22  
322             my ( $coerce, $env );
323 11     23   52 ( $coerce, $arg_name, $env ) = $self->_inline_coercion($arg_name);
  23         83  
324             my ( $assert, $assert_env ) = $self->inline_assert($arg_name);
325              
326             $source .= $coerce;
327 2     2 0 657 $source .= $assert;
328 2         3 $source .= $arg_name . '};';
329              
330 2 50       5 return ( $source, { %{$env}, %{$assert_env} } );
331             }
332              
333 2         17 my $self = shift;
334             my $arg_name = shift;
335 2         5  
336 2         4 return ( q{}, $arg_name, {} ) unless $self->has_coercions;
337 2         7  
338             my %env;
339 2         7  
340             $arg_name = '$value';
341             my $source = $arg_name . ' = ';
342             for my $coercion ( $self->coercions ) {
343 6     6 0 579 $source
344 6         13 .= '('
345             . $coercion->from->inline_check($arg_name) . ') ? ('
346 6 50       17 . $coercion->inline_coercion($arg_name) . ') : ';
347              
348             %env = (
349 6         61 %env,
350             %{ $coercion->inline_environment },
351 6         12 %{ $coercion->from->inline_environment },
352 6         20 );
353 6         21 }
354             $source .= $arg_name . ';';
355 6         14  
356 6         12 return ( $source, $arg_name, \%env );
357 6         11 }
358              
359 6         9 {
  6         11  
  6         37  
360             my $counter = 1;
361              
362             my $self = shift;
363 8     8   14  
364 8         11 my $type_var_name = '$_Specio_Constraint_Interface_type' . $counter;
365             my $message_generator_var_name
366 8 100       19 = '$_Specio_Constraint_Interface_message_generator' . $counter;
367             my %env = (
368 4         6 $type_var_name => \$self,
369             $message_generator_var_name => \( $self->_message_generator ),
370 4         7 %{ $self->inline_environment },
371 4         7 );
372 4         9  
373 9         43 my $source = $self->inline_check( $_[0] );
374             $source .= ' or ';
375             $source .= $self->_inline_throw_exception(
376             $_[0],
377             $message_generator_var_name,
378             $type_var_name
379             );
380 9         18 $source .= ';';
381 9         55  
  9         40  
382             $counter++;
383              
384 4         24 return ( $source, \%env );
385             }
386 4         14 }
387              
388             my $self = shift;
389              
390             die 'Cannot inline' unless $self->can_be_inlined;
391              
392             my $type = $self->_self_or_first_inlinable_ancestor;
393 126     126 0 248 return $type->_inline_generator->( $type, @_ );
394             }
395 126         299  
396 126         236 # For some idiotic reason I called $type->_subify directly in Code::TidyAll so
397             # I'll leave this in here for now.
398              
399             ## no critic (Subroutines::ProhibitUnusedPrivateSubroutines)
400             ## use critic
401 126         513  
  126         635  
402             my $self = shift;
403              
404 126         1132 if ( defined &Sub::Quote::quote_sub && $self->can_be_inlined ) {
405 126         422 return Sub::Quote::quote_sub( $self->inline_assert('$_[0]') );
406 126         393 }
407             else {
408             return sub { $self->validate_or_die( $_[0] ) };
409             }
410             }
411 126         210  
412             shift;
413 126         203 my $value_var = shift;
414             my $message_generator_var_name = shift;
415 126         470 my $type_var_name = shift;
416              
417             #<<<
418             return 'Specio::Exception->throw( '
419             . ' message => ' . $message_generator_var_name . '->(' . $value_var . '),'
420 468     468 0 3438 . ' type => ' . $type_var_name . ','
421             . ' value => ' . $value_var . ' )';
422 468 50       729 #>>>
423             }
424 468         2292  
425 468         1144 # This exists for the benefit of Moo
426             my $self = shift;
427              
428             if ( defined &Sub::Quote::quote_sub
429             && all { $_->can_be_inlined } $self->coercions ) {
430              
431             my $inline = q{};
432 0     0   0 my %env;
433              
434             for my $coercion ( $self->coercions ) {
435             $inline .= sprintf(
436 78     78   17970 '$_[0] = %s if %s;' . "\n",
437             $coercion->inline_coercion('$_[0]'),
438 78 100 66     386 $coercion->from->inline_check('$_[0]')
439 72         713 );
440              
441             %env = (
442 6     217   122 %env,
  217         3171  
443             %{ $coercion->inline_environment },
444             %{ $coercion->from->inline_environment },
445             );
446             }
447 126     126   188  
448 126         200 $inline .= sprintf( "%s;\n", '$_[0]' );
449 126         187  
450 126         182 return Sub::Quote::quote_sub( $inline, \%env );
451             }
452             else {
453 126         509 return sub { $self->coerce_value(shift) };
454             }
455             }
456              
457             my $self = shift;
458              
459             my @parents;
460              
461             my $type = $self;
462 5     5 0 6 while ( $type = $type->parent ) {
463             push @parents, $type;
464 5 100 66     24 }
465 6     6   18  
466             return \@parents;
467 3         19  
468 3         4 }
469              
470 3         6 my $self = shift;
471 4         25  
472             my $desc
473             = $self->is_anon ? 'anonymous type' : 'type named ' . $self->name;
474              
475             $desc .= q{ } . $self->declared_at->description;
476              
477             return $desc;
478             }
479 4         12  
480 4         10  
  4         20  
481             ## no critic (Subroutines::ProhibitUnusedPrivateSubroutines)
482             my $self = shift;
483              
484 3         16 my $coercions = $self->_coercions;
485             my %clones;
486 3         8  
487             for my $name ( keys %{$coercions} ) {
488             my $coercion = $coercions->{$name};
489 2     3   28 $clones{$name} = $coercion->clone_with_new_to($self);
  3         1905  
490             }
491              
492             return \%clones;
493             }
494 449     449   2069 ## use critic
495              
496 449         582 my $self = shift;
497              
498 449         584 return $self->name unless $self->is_anon;
499 449         1098  
500 1148         2689 return sprintf( '__ANON__(%s)', $self->parent . q{} );
501             }
502              
503 449         2388 my $self = shift;
504              
505             # This assumes that when a type is cloned, the underlying constraint or
506             # generator sub is copied by _reference_, so it has the same memory
507             # address and stringifies to the same value. XXX - will this break under
508 923     923   4009 # threads?
509             return join "\n",
510 923 100       1668 ( $self->_has_parent ? $self->parent->_signature : () ),
511             (
512             defined $self->_constraint
513 923         7368 ? $self->_constraint
514             : $self->_inline_generator
515 923         2373 );
516             }
517              
518 927     927   17669 # Moose compatibility methods - these exist as a temporary hack to make Specio
519             # work with Moose.
520              
521             shift->has_coercions;
522 9360     9360   11605 }
523              
524 9360         15118 ## no critic (Subroutines::ProhibitUnusedPrivateSubroutines)
525 9360         23709 shift->inline_check(@_);
526             }
527 9360         9947  
  9360         14857  
528 1         2 shift->_optimized_constraint;
529 1         3 }
530             ## use critic;
531              
532 9360         19721 # This class implements the methods that Moose expects from coercions as well.
533             return shift;
534             }
535              
536             ## no critic (Subroutines::ProhibitUnusedPrivateSubroutines)
537 54     54   30925 my $self = shift;
538              
539 54 100       115 return sub {
540             return $self->coerce_value(shift);
541 11         55 };
542             }
543             ## use critic
544              
545 82     82   673 1;
546             }
547              
548             shift->_message_generator;
549             }
550              
551 82 100       152 my $self = shift;
    100          
552             my $value = shift;
553              
554             return $self->_message_generator->( $self, $value );
555             }
556              
557             shift->value_is_valid(@_);
558             }
559              
560             shift->coerce_value(@_);
561             }
562              
563             1;
564 0     0 0 0  
565             # ABSTRACT: The interface all type constraints should provide
566              
567              
568             =pod
569 114     114   350  
570             =encoding UTF-8
571              
572             =head1 NAME
573 0     0   0  
574             Specio::Constraint::Role::Interface - The interface all type constraints should provide
575              
576             =head1 VERSION
577              
578             version 0.48
579 0     0 0 0  
580             =head1 DESCRIPTION
581              
582             This role defines the interface that all type constraints must provide, and
583             provides most (or all) of the implementation. The L<Specio::Constraint::Simple>
584 0     0   0 class simply consumes this role and provides no additional code. Other
585             constraint classes add features or override some of this role's functionality.
586              
587 0     0   0 =for Pod::Coverage .*
588 0         0  
589             =head1 API
590              
591             See the L<Specio::Constraint::Simple> documentation for details. See the
592             internals of various constraint classes to see how this role can be overridden
593 0     0 0 0 or expanded upon.
594              
595             =head1 ROLES
596              
597 0     0 0 0 This role does the L<Specio::Role::Inlinable> role.
598              
599             =head1 SUPPORT
600              
601 0     0 0 0 Bugs may be submitted at L<https://github.com/houseabsolute/Specio/issues>.
602 0         0  
603             =head1 SOURCE
604 0         0  
605             The source code repository for Specio can be found at L<https://github.com/houseabsolute/Specio>.
606              
607             =head1 AUTHOR
608 50     50 0 109  
609             Dave Rolsky <autarch@urth.org>
610              
611             =head1 COPYRIGHT AND LICENSE
612 0     0 0    
613             This software is Copyright (c) 2012 - 2022 by Dave Rolsky.
614              
615             This is free software, licensed under:
616              
617             The Artistic License 2.0 (GPL Compatible)
618              
619             The full text of the license can be found in the
620             F<LICENSE> file included with this distribution.
621              
622             =cut