File Coverage

blib/lib/Type/Utils.pm
Criterion Covered Total %
statement 378 385 98.1
branch 166 196 84.6
condition 46 75 61.3
subroutine 49 49 100.0
pod 24 24 100.0
total 663 729 90.9


line stmt bran cond sub pod time code
1             package Type::Utils;
2              
3 122     122   370148 use 5.008001;
  122         555  
4 122     121   823 use strict;
  121         537  
  121         4054  
5 121     105   704 use warnings;
  105         291  
  105         5183  
6              
7             BEGIN {
8 105     105   402 $Type::Utils::AUTHORITY = 'cpan:TOBYINK';
9 105         8571 $Type::Utils::VERSION = '2.003_000';
10             }
11              
12             $Type::Utils::VERSION =~ tr/_//d;
13              
14 5     5   37 sub _croak ($;@) { require Error::TypeTiny; goto \&Error::TypeTiny::croak }
  5         42  
15              
16 105     105   879 use Scalar::Util qw< blessed >;
  105         963  
  105         6568  
17 105     105   16276 use Type::Library;
  105         286  
  105         1010  
18 105     105   4125 use Type::Tiny;
  105         292  
  105         3496  
19 105     105   690 use Types::TypeTiny qw< TypeTiny is_TypeTiny to_TypeTiny HashLike StringLike >;
  105         247  
  105         877  
20              
21             our @EXPORT = qw<
22             declare as where message inline_as
23             class_type role_type duck_type union intersection enum
24             coerce from via
25             declare_coercion to_type
26             >;
27             our @EXPORT_OK = (
28             @EXPORT,
29             qw<
30             extends type subtype
31             match_on_type compile_match_on_type
32             dwim_type english_list
33             classifier assert
34             >,
35             "is",
36             );
37             our %EXPORT_TAGS = (
38             default => [@EXPORT],
39             all => [@EXPORT_OK],
40             );
41             pop @{ $EXPORT_TAGS{all} }; # remove 'is'
42              
43             require Exporter::Tiny;
44             our @ISA = 'Exporter::Tiny';
45              
46             sub extends {
47 68 50   68 1 24704 _croak "Not a type library" unless caller->isa( "Type::Library" );
48 68         373 my $caller = caller->meta;
49            
50 68         190 foreach my $lib ( @_ ) {
51 115 50   43   7953 eval "use $lib; 1" or _croak "Could not load library '$lib': $@";
  43     39   19680  
  43         565  
  43         389  
  39         19339  
  39         165  
  39         522  
52            
53 115 100 100     1594 if ( $lib->isa( "Type::Library" ) or $lib eq 'Types::TypeTiny' ) {
    100 66        
    100          
    50          
    100          
54 109         521 $caller->add_type( $lib->get_type( $_ ) ) for sort $lib->meta->type_names;
55             $caller->add_coercion( $lib->get_coercion( $_ ) )
56 109         921 for sort $lib->meta->coercion_names;
57             }
58             elsif ( $lib->isa( 'MooseX::Types::Base' ) ) {
59 2         12 require Moose::Util::TypeConstraints;
60 2         9 my $types = $lib->type_storage;
61 2         24 for my $name ( sort keys %$types ) {
62             my $moose =
63 22         104 Moose::Util::TypeConstraints::find_type_constraint( $types->{$name} );
64 22         2348 my $tt = Types::TypeTiny::to_TypeTiny( $moose );
65 22   66     743 my $c = $moose->has_coercion && @{ $moose->coercion->type_coercion_map || [] };
66 22 100       269 $caller->add_type(
67             $tt->create_child_type(
68             library => $caller, name => $name, coercion => $c ? 1 : 0
69             )
70             );
71             } #/ for my $name ( sort keys...)
72             } #/ elsif ( $lib->isa( 'MooseX::Types::Base'...))
73             elsif ( $lib->isa( 'MouseX::Types::Base' ) ) {
74 1         5 require Mouse::Util::TypeConstraints;
75 1         3 my $types = $lib->type_storage;
76 1         10 for my $name ( sort keys %$types ) {
77             my $mouse =
78 21         72 Mouse::Util::TypeConstraints::find_type_constraint( $types->{$name} );
79 21         265 my $tt = Types::TypeTiny::to_TypeTiny( $mouse );
80 21 50       95 $caller->add_type(
81             $tt->create_child_type(
82             library => $caller, name => $name, coercion => $mouse->has_coercion ? 1 : 0
83             )
84             );
85             } #/ for my $name ( sort keys...)
86             } #/ elsif ( $lib->isa( 'MouseX::Types::Base'...))
87             elsif ( $lib->isa( 'Specio::Exporter' ) ) {
88 0         0 my $types = $lib->Specio::Registry::exportable_types_for_package;
89 0         0 for my $name ( sort keys %$types ) {
90 0         0 my $specio = $types->{$name};
91 0         0 my $tt = Types::TypeTiny::to_TypeTiny( $specio );
92 0         0 $caller->add_type(
93             $tt->create_child_type( library => $caller, name => $name )
94             );
95             }
96             }
97             elsif ( $lib->isa( 'Exporter' )
98 105     105   134010 and my $types = do { no strict 'refs'; ${"$lib\::EXPORT_TAGS"}{'types'} } ) {
  105         265  
  105         89629  
  1         1  
  1         7  
99 1         2 for my $name ( @$types ) {
100 3         20 my $obj = $lib->$name;
101 3         30 my $tt = Types::TypeTiny::to_TypeTiny( $obj );
102 3         12 $caller->add_type(
103             $tt->create_child_type( library => $caller, name => $name )
104             );
105             }
106             }
107             else {
108 2         14 _croak( "'$lib' is not a type constraint library" );
109             }
110             } #/ foreach my $lib ( @_ )
111             } #/ sub extends
112              
113             sub declare {
114 336     336 1 523 my %opts;
115 336 100       1167 if ( @_ % 2 == 0 ) {
116 188         642 %opts = @_;
117 188 100 100     687 if ( @_ == 2 and $_[0] =~ /^_*[A-Z]/ and $_[1] =~ /^[0-9]+$/ ) {
      66        
118 1         7 require Carp;
119 1         4 Carp::carp( "Possible missing comma after 'declare $_[0]'" );
120             }
121             }
122             else {
123 148         540 ( my ( $name ), %opts ) = @_;
124 148 50       436 _croak "Cannot provide two names for type" if exists $opts{name};
125 148         337 $opts{name} = $name;
126             }
127            
128 336   100     1410 my $caller = caller( $opts{_caller_level} || 0 );
129 336         679 $opts{library} = $caller;
130            
131 336 100       840 if ( defined $opts{parent} ) {
132 128         556 $opts{parent} = to_TypeTiny( $opts{parent} );
133            
134 128 100       2604 unless ( is_TypeTiny( $opts{parent} ) ) {
135             $caller->isa( "Type::Library" )
136             or _croak(
137             "Parent type cannot be a %s",
138 88 50 0     844 ref( $opts{parent} ) || 'non-reference scalar'
139             );
140             $opts{parent} = $caller->meta->get_type( $opts{parent} )
141 88 50       289 or _croak( "Could not find parent type" );
142             }
143             } #/ if ( defined $opts{parent...})
144            
145 336         548 my $type;
146 336 100       792 if ( defined $opts{parent} ) {
147 128         743 $type = delete( $opts{parent} )->create_child_type( %opts );
148             }
149             else {
150 208   100     673 my $bless = delete( $opts{bless} ) || "Type::Tiny";
151 208         12045 eval "require $bless";
152 208         1757 $type = $bless->new( %opts );
153             }
154            
155 336 100       1556 if ( not $type->is_anon ) {
156            
157 306 100       2427 $caller->meta->add_type( $type )
158             if $caller->isa( 'Type::Library' );
159            
160             $INC{'Type/Registry.pm'}
161             ? 'Type::Registry'->for_class( $caller )->add_type( $type, $opts{name} )
162 305 100       1296 : ( $Type::Registry::DELAYED{$caller}{$opts{name}} = $type );
163             }
164            
165 335         8469 return $type;
166             } #/ sub declare
167              
168             *subtype = \&declare;
169             *type = \&declare;
170              
171             sub as (@) {
172 129     129 1 7177 parent => @_;
173             }
174              
175             sub where (&;@) {
176 119     119 1 4248 constraint => @_;
177             }
178              
179             sub message (&;@) {
180 68     68 1 6439 message => @_;
181             }
182              
183             sub inline_as (&;@) {
184 1     1 1 6 inlined => @_;
185             }
186              
187             sub class_type {
188 67 100   67 1 906 my $name = ref( $_[0] ) eq 'HASH' ? undef : shift;
189 67 100       119 my %opts = %{ shift or {} };
  67         393  
190            
191 67 100       227 if ( defined $name ) {
192 56 50       216 $opts{name} = $name unless exists $opts{name};
193 56 100       394 $opts{class} = $name unless exists $opts{class};
194            
195 56         247 $opts{name} =~ s/:://g;
196             }
197            
198 67         172 $opts{bless} = "Type::Tiny::Class";
199            
200 105     105   1024 { no warnings "numeric"; $opts{_caller_level}++ }
  105         271  
  105         21489  
  67         107  
  67         168  
201 67         239 declare( %opts );
202             } #/ sub class_type
203              
204             sub role_type {
205 26 100   26 1 165 my $name = ref( $_[0] ) eq 'HASH' ? undef : shift;
206 26 50       57 my %opts = %{ shift or {} };
  26         161  
207            
208 26 100       107 if ( defined $name ) {
209 25 50       144 $opts{name} = $name unless exists $opts{name};
210 25 100       148 $opts{role} = $name unless exists $opts{role};
211            
212 25         98 $opts{name} =~ s/:://g;
213             }
214            
215 26         77 $opts{bless} = "Type::Tiny::Role";
216            
217 105     105   941 { no warnings "numeric"; $opts{_caller_level}++ }
  105         305  
  105         16203  
  26         58  
  26         75  
218 26         116 declare( %opts );
219             } #/ sub role_type
220              
221             sub duck_type {
222 49 50   49 1 1115 my $name = ref( $_[0] ) eq 'ARRAY' ? undef : shift;
223 49 50       104 my @methods = @{ shift or [] };
  49         222  
224            
225 49         90 my %opts;
226 49 50       172 $opts{name} = $name if defined $name;
227 49         113 $opts{methods} = \@methods;
228            
229 49         108 $opts{bless} = "Type::Tiny::Duck";
230            
231 105     105   864 { no warnings "numeric"; $opts{_caller_level}++ }
  105         264  
  105         15648  
  49         90  
  49         95  
232 49         208 declare( %opts );
233             } #/ sub duck_type
234              
235             sub enum {
236 20 100   20 1 6205 my $name = ref( $_[0] ) eq 'ARRAY' ? undef : shift;
237 20 50       43 my @values = @{ shift or [] };
  20         92  
238            
239 20         40 my %opts;
240 20 100       88 $opts{name} = $name if defined $name;
241 20         55 $opts{values} = \@values;
242            
243 20         44 $opts{bless} = "Type::Tiny::Enum";
244            
245 105     105   794 { no warnings "numeric"; $opts{_caller_level}++ }
  105         247  
  105         15901  
  20         42  
  20         45  
246 20         82 declare( %opts );
247             } #/ sub enum
248              
249             sub union {
250 14 100   14 1 2695 my $name = ref( $_[0] ) eq 'ARRAY' ? undef : shift;
251 14 50       37 my @tcs = @{ shift or [] };
  14         69  
252            
253 14         31 my %opts;
254 14 100       55 $opts{name} = $name if defined $name;
255 14         49 $opts{type_constraints} = \@tcs;
256            
257 14         39 $opts{bless} = "Type::Tiny::Union";
258            
259 105     105   1012 { no warnings "numeric"; $opts{_caller_level}++ }
  105         320  
  105         15273  
  14         30  
  14         31  
260 14         79 declare( %opts );
261             } #/ sub union
262              
263             sub intersection {
264 8 100   8 1 182 my $name = ref( $_[0] ) eq 'ARRAY' ? undef : shift;
265 8 50       17 my @tcs = @{ shift or [] };
  8         33  
266            
267 8         18 my %opts;
268 8 100       28 $opts{name} = $name if defined $name;
269 8         21 $opts{type_constraints} = \@tcs;
270            
271 8         19 $opts{bless} = "Type::Tiny::Intersection";
272            
273 105     105   856 { no warnings "numeric"; $opts{_caller_level}++ }
  105         246  
  105         261383  
  8         15  
  8         19  
274 8         36 declare( %opts );
275             } #/ sub intersection
276              
277             sub declare_coercion {
278 45     45 1 76 my %opts;
279 45 100       270 $opts{name} = shift if !ref( $_[0] );
280            
281             # I don't like this; it is a hack
282 45 100       239 if ( ref( $_[0] ) eq 'Type::Tiny::_DeclaredType' ) {
283 1         6 $opts{name} = '' . shift;
284             }
285            
286 45   66     946 while ( Types::TypeTiny::is_HashLike( $_[0] ) and not is_TypeTiny( $_[0] ) ) {
287 45         458 %opts = ( %opts, %{ +shift } );
  45         327  
288             }
289            
290 45   50     269 my $caller = caller( $opts{_caller_level} || 0 );
291 45         147 $opts{library} = $caller;
292            
293 45   50     226 my $bless = delete( $opts{bless} ) || "Type::Coercion";
294 45         2628 eval "require $bless";
295 45         367 my $c = $bless->new( %opts );
296            
297 45         99 my @C;
298            
299 45 100       242 if ( $caller->isa( "Type::Library" ) ) {
300 44         165 my $meta = $caller->meta;
301 44 100       155 $meta->add_coercion( $c ) unless $c->is_anon;
302 44         175 while ( @_ ) {
303             push @C,
304 44 100 33     138 map { ref( $_ ) ? to_TypeTiny( $_ ) : $meta->get_type( $_ ) || $_ } shift;
  44         200  
305 44         178 push @C, shift;
306             }
307             }
308             else {
309 1         4 @C = @_;
310             }
311            
312 45         240 $c->add_type_coercions( @C );
313            
314 45         185 return $c->freeze;
315             } #/ sub declare_coercion
316              
317             sub coerce {
318 64 100   64 1 368 if ( ( scalar caller )->isa( "Type::Library" ) ) {
319 47         184 my $meta = ( scalar caller )->meta;
320             my ( $type ) =
321 47 100 33     167 map { ref( $_ ) ? to_TypeTiny( $_ ) : $meta->get_type( $_ ) || $_ } shift;
  47         566  
322 47         116 my @opts;
323 47         180 while ( @_ ) {
324             push @opts,
325 89 100 33     156 map { ref( $_ ) ? to_TypeTiny( $_ ) : $meta->get_type( $_ ) || $_ } shift;
  89         258  
326 89         227 push @opts, shift;
327             }
328 47         199 return $type->coercion->add_type_coercions( @opts );
329             } #/ if ( ( scalar caller )...)
330            
331 17         54 my ( $type, @opts ) = @_;
332 17         42 $type = to_TypeTiny( $type );
333 17         122 return $type->coercion->add_type_coercions( @opts );
334             } #/ sub coerce
335              
336             sub from (@) {
337 151     151 1 947 return @_;
338             }
339              
340             sub to_type (@) {
341 45     45 1 93 my $type = shift;
342 45 100       696 unless ( is_TypeTiny( $type ) ) {
343 42 50       453 caller->isa( "Type::Library" )
344             or _croak "Target type cannot be a string";
345 42 50       183 $type = caller->meta->get_type( $type )
346             or _croak "Could not find target type";
347             }
348 45         417 return +{ type_constraint => $type }, @_;
349             } #/ sub to_type (@)
350              
351             sub via (&;@) {
352 93     93 1 892 return @_;
353             }
354              
355             sub match_on_type {
356 40017     40017 1 87327 my $value = shift;
357            
358 40017         94549 while ( @_ ) {
359 115043         185167 my $code;
360 115043 100       213077 if ( @_ == 1 ) {
361 1         2 $code = shift;
362             }
363             else {
364 115042         225052 ( my ( $type ), $code ) = splice( @_, 0, 2 );
365 115042 100       2382784 Types::TypeTiny::assert_TypeTiny( $type )->check( $value ) or next;
366             }
367            
368 40016 100       223187 if ( Types::TypeTiny::is_StringLike( $code ) ) {
369 30010         53292 local $_ = $value;
370 30010 100       62680 if ( wantarray ) {
371 15004         833886 my @r = eval "$code";
372 15004 50       56687 die $@ if $@;
373 15004         105586 return @r;
374             }
375 15006 100       27684 if ( defined wantarray ) {
376 15005         687325 my $r = eval "$code";
377 15005 50       56537 die $@ if $@;
378 15005         109798 return $r;
379             }
380 1         62 eval "$code";
381 1 50       27 die $@ if $@;
382 1         4 return;
383             } #/ if ( Types::TypeTiny::is_StringLike...)
384             else {
385 10006         31032 Types::TypeTiny::assert_CodeLike( $code );
386 10006         45965 local $_ = $value;
387 10006         26404 return $code->( $value );
388             }
389             } #/ while ( @_ )
390            
391 1         14 _croak( "No cases matched for %s", Type::Tiny::_dd( $value ) );
392             } #/ sub match_on_type
393              
394             sub compile_match_on_type {
395 5     5 1 1497 require Eval::TypeTiny::CodeAccumulator;
396 5         49 my $coderef = 'Eval::TypeTiny::CodeAccumulator'->new(
397             description => 'compiled match',
398             );
399 5         22 $coderef->add_line( 'sub {' );
400 5         29 $coderef->increase_indent;
401 5         21 $coderef->add_line( 'local $_ = $_[0];' );
402            
403 5         12 my $els = '';
404            
405 5         29 while ( @_ ) {
406 20         37 my ( $type, $code );
407 20 100       51 if ( @_ == 1 ) {
408 2         16 require Types::Standard;
409 2         17 ( $type, $code ) = ( Types::Standard::Any(), shift );
410             }
411             else {
412 18         44 ( $type, $code ) = splice( @_, 0, 2 );
413 18         430 Types::TypeTiny::assert_TypeTiny( $type );
414             }
415            
416 20 100       82 if ( $type->can_be_inlined ) {
417 19         65 $coderef->add_line( sprintf(
418             '%sif ( %s ) {',
419             $els,
420             $type->inline_check( '$_' ),
421             ) );
422             }
423             else {
424 1         10 my $varname = $coderef->add_variable( '$type', \$type );
425 1         10 $coderef->add_line( sprintf(
426             '%sif ( %s->check($_) ) {',
427             $els,
428             $varname,
429             ) );
430             }
431 20         75 $coderef->increase_indent;
432            
433 20         50 $els = 'els';
434            
435 20 100       69 if ( Types::TypeTiny::is_StringLike( $code ) ) {
436 5         13 $coderef->add_line( $code );
437             }
438             else {
439 15         52 Types::TypeTiny::assert_CodeLike( $code );
440 15         112 my $varname = $coderef->add_variable( '$action', \$code );
441 15         58 $coderef->add_line( sprintf(
442             '%s->( @_ )',
443             $varname,
444             ) );
445             }
446 20         71 $coderef->decrease_indent;
447 20         53 $coderef->add_line( '}' );
448             } #/ while ( @_ )
449            
450 5         25 $coderef->add_line( 'else {' );
451 5         19 $coderef->increase_indent;
452 5         15 $coderef->add_line( 'Type::Utils::_croak( "No cases matched for %s", Type::Tiny::_dd( $_ ) );' );
453 5         21 $coderef->decrease_indent;
454 5         16 $coderef->add_line( '}' );
455            
456 5         20 $coderef->decrease_indent;
457 5         26 $coderef->add_line( '}' );
458            
459 5         20 return $coderef->compile;
460             } #/ sub compile_match_on_type
461              
462             sub classifier {
463 1     1 1 8 my $i;
464             compile_match_on_type(
465             +(
466             map {
467 8         13 my $type = $_->[0];
468 8     10   21 $type => sub { $type };
  10         5040  
469             }
470 1 50       6 sort { $b->[1] <=> $a->[1] or $a->[2] <=> $b->[2] }
  17         33  
471             map [ $_, scalar( my @parents = $_->parents ), ++$i ],
472             @_
473             ),
474             q[ undef ],
475             );
476             } #/ sub classifier
477              
478             {
479             package #hide
480             Type::Registry::DWIM;
481            
482             our @ISA = qw(Type::Registry);
483            
484             sub foreign_lookup {
485 13     13   28 my $self = shift;
486 13         61 my $r = $self->SUPER::foreign_lookup( @_ );
487 13 100       46 return $r if $r;
488            
489 9 50 33     119 if ( my $assume = $self->{"~~assume"}
490             and $_[0] =~ /[A-Z_a-z][0-9A-Z_a-z]*(?:::[0-9A-Z_a-z]+)*/ )
491             {
492 9 50       45 my @methods = ref( $assume ) ? @$assume : $assume;
493            
494 9         33 for my $method ( @methods ) {
495 21         89 $r = $self->$method( @_ );
496 21 100       49 return $r if $r;
497             }
498             } #/ if ( my $assume = $self...)
499            
500 2         6 return;
501             } #/ sub foreign_lookup
502            
503             sub lookup_via_moose {
504 10     10   20 my $self = shift;
505            
506 10 100       31 if ( $INC{'Moose.pm'} ) {
507 5         55 require Moose::Util::TypeConstraints;
508 5         32 require Types::TypeTiny;
509 5         22 my $r = Moose::Util::TypeConstraints::find_type_constraint( $_[0] );
510 5 100       463 return Types::TypeTiny::to_TypeTiny( $r ) if defined $r;
511             }
512            
513 7         16 return;
514             } #/ sub lookup_via_moose
515            
516             sub lookup_via_mouse {
517 10     10   21 my $self = shift;
518            
519 10 100       32 if ( $INC{'Mouse.pm'} ) {
520 5         33 require Mouse::Util::TypeConstraints;
521 5         21 require Types::TypeTiny;
522 5         27 my $r = Mouse::Util::TypeConstraints::find_type_constraint( $_[0] );
523 5 100       85 return Types::TypeTiny::to_TypeTiny( $r ) if defined $r;
524             }
525            
526 7         13 return;
527             } #/ sub lookup_via_mouse
528            
529             sub simple_lookup {
530 172     172   315 my $self = shift;
531 172         290 my $r;
532            
533             # If the lookup is chained to a class, then the class' own
534             # type registry gets first refusal.
535             #
536 172 50       546 if ( defined $self->{"~~chained"} ) {
537 172         1029 my $chained = "Type::Registry"->for_class( $self->{"~~chained"} );
538 172 50       596 $r = eval { $chained->simple_lookup( @_ ) } unless $self == $chained;
  172         578  
539 172 100       495 return $r if defined $r;
540             }
541            
542             # Fall back to types in Types::Standard.
543 169         2691 require Types::Standard;
544 169 100       1181 return 'Types::Standard'->get_type( $_[0] )
545             if 'Types::Standard'->has_type( $_[0] );
546            
547             # Only continue any further if we've been called from Type::Parser.
548 54 100       469 return unless $_[1];
549            
550 9         24 my $meta;
551 9 50       35 if ( defined $self->{"~~chained"} ) {
552 9 100 66     75 $meta ||= Moose::Util::find_meta( $self->{"~~chained"} ) if $INC{'Moose.pm'};
553 9 100 66     169 $meta ||= Mouse::Util::find_meta( $self->{"~~chained"} ) if $INC{'Mouse.pm'};
554             }
555            
556 9 100 100     171 if ( $meta and $meta->isa( 'Class::MOP::Module' ) ) {
    100 66        
557 3         17 $r = $self->lookup_via_moose( @_ );
558 3 50       17 return $r if $r;
559             }
560            
561             elsif ( $meta and $meta->isa( 'Mouse::Meta::Module' ) ) {
562 3         15 $r = $self->lookup_via_mouse( @_ );
563 3 50       14 return $r if $r;
564             }
565            
566 3         9 return $self->foreign_lookup( @_ );
567             } #/ sub simple_lookup
568             }
569              
570             our $dwimmer;
571              
572             sub dwim_type {
573 110     110 1 10210 my ( $string, %opts ) = @_;
574 110 100       503 $opts{for} = caller unless defined $opts{for};
575            
576 110   66     455 $dwimmer ||= do {
577 9         3682 require Type::Registry;
578 9         91 'Type::Registry::DWIM'->new;
579             };
580            
581 110         407 local $dwimmer->{'~~chained'} = $opts{for};
582             local $dwimmer->{'~~assume'} = $opts{fallback} || [
583             qw/ lookup_via_moose lookup_via_mouse /,
584 110   100     971 $opts{does} ? 'make_role_type' : 'make_class_type',
585             ];
586            
587 110         353 local $@ = undef;
588 110         190 my $type;
589 110 100       248 unless ( eval { $type = $dwimmer->lookup( $string ); 1 } ) {
  110         588  
  107         446  
590 3         10 my $e = $@;
591 3 100       83 die( $e ) unless $e =~ /not a known type constraint/;
592             }
593            
594 109         811 $type;
595             } #/ sub dwim_type
596              
597             my $TEMPLATE = <<'SUBTEMPLATE';
598             sub SUBNAME
599             {
600             require Types::TypeTiny;
601             no warnings 'uninitialized';
602            
603             my ($type, $value) = @_;
604             my $caller = caller;
605            
606             my $uniq = Types::TypeTiny::is_TypeTiny($type) ? $type->{uniq} : "$type";
607            
608             if (not Types::TypeTiny::is_TypeTiny $type) {
609             my $orig = $type;
610            
611             $type = $is_cache{$caller}{$uniq} || do {
612             Types::TypeTiny::is_StringLike($type)
613             ? eval { dwim_type("$type", for => $caller) }
614             : undef;
615             };
616            
617             if (blessed $type) {
618             $is_cache{$caller}{$uniq} ||= $type;
619             }
620             else {
621             my $thing = Type::Tiny::_dd($orig);
622             substr($thing, 0, 1) = lc substr($thing, 0, 1);
623             require Carp;
624             FAILURE
625             }
626             }
627            
628             my $check = ( $is_cache_coderef{$caller}{$uniq} ||= $type->compiled_check );
629            
630             BODY
631             }
632             SUBTEMPLATE
633              
634             my %is_cache;
635             my %is_cache_coderef;
636              
637             {
638             my $code = $TEMPLATE;
639             $code =~ s/SUBNAME/is/g;
640             $code =~
641             s/FAILURE/Carp::carp("Expected type, but got \$thing; returning false"); return undef;/g;
642             $code =~ s/BODY/0+!! \$check->(\$value)/;
643 105 100 100 105 1 800 eval $code;
  105 100 66 6   437  
  105 100 66     27379  
  6         1294  
  6         29  
  6         13  
  6         152  
  6         200  
  4         83  
  4         16  
  4         18  
  2         10  
  2         7  
  2         15  
  2         9  
  2         309  
  2         71  
  4         23  
  4         34  
644             }
645              
646             {
647             my $code = $TEMPLATE;
648             $code =~ s/SUBNAME/assert/g;
649             $code =~
650             s/FAILURE/Carp::croak("Expected type, but got \$thing; stopping"); return undef;/g;
651             $code =~
652             s/BODY/\$check->(\$value) ? \$value : \$type->_failed_check("\$type", \$value)/;
653 105 100 33 105 1 800 eval $code;
  105 50 0 3   263  
  105 100 33     27704  
  3 100       1035  
  3         9  
  3         9  
  3         59  
  3         76  
  1         20  
  1         6  
  1         27  
  0         0  
  1         14  
  1         6  
  1         5  
  1         101  
  0         0  
  2         10  
  2         32  
654             }
655              
656             sub english_list {
657 107 100   107 1 4203 my $conjunction = ref( $_[0] ) eq 'SCALAR' ? ${ +shift } : 'and';
  2         6  
658 107         315 my @items = sort @_;
659            
660 107 100       520 return $items[0] if @items == 1;
661 75 100       515 return "$items[0] $conjunction $items[1]" if @items == 2;
662            
663 33         104 my $tail = pop @items;
664 33         315 join( ', ', @items, "$conjunction $tail" );
665             } #/ sub english_list
666              
667             1;
668              
669             __END__
670              
671             =pod
672              
673             =encoding utf-8
674              
675             =for stopwords smush smushed
676              
677             =head1 NAME
678              
679             Type::Utils - utility functions to make defining and using type constraints a little easier
680              
681             =head1 SYNOPSIS
682              
683             package Types::Mine;
684            
685             use Type::Library -base;
686             use Type::Utils -all;
687            
688             BEGIN { extends "Types::Standard" };
689            
690             declare "AllCaps",
691             as "Str",
692             where { uc($_) eq $_ },
693             inline_as { my $varname = $_[1]; "uc($varname) eq $varname" };
694            
695             coerce "AllCaps",
696             from "Str", via { uc($_) };
697              
698             =head1 STATUS
699              
700             This module is covered by the
701             L<Type-Tiny stability policy|Type::Tiny::Manual::Policies/"STABILITY">.
702              
703             =head1 DESCRIPTION
704              
705             This module provides utility functions to make defining and using type
706             constraints a little easier.
707              
708             =head2 Type declaration functions
709              
710             Many of the following are similar to the similarly named functions described
711             in L<Moose::Util::TypeConstraints>.
712              
713             =over
714              
715             =item C<< declare $name, %options >>
716              
717             =item C<< declare %options >>
718              
719             Declare a named or anonymous type constraint. Use C<as> and C<where> to
720             specify the parent type (if any) and (possibly) refine its definition.
721              
722             declare EvenInt, as Int, where { $_ % 2 == 0 };
723              
724             my $EvenInt = declare as Int, where { $_ % 2 == 0 };
725              
726             I<< NOTE: >>
727             Named types will be automatically added to the caller's type registry.
728             (See L<Type::Registry>.) If the caller package inherits from L<Type::Library>
729             named types will also be automatically installed into the library and
730             made available as exports.
731              
732             Hidden gem: if you're inheriting from a type constraint that includes some
733             coercions, you can include C<< coercion => 1 >> in the C<< %options >> hash
734             to inherit the coercions.
735              
736             =item C<< subtype $name, %options >>
737              
738             =item C<< subtype %options >>
739              
740             Declare a named or anonymous type constraint which is descended from an
741             existing type constraint. Use C<as> and C<where> to specify the parent
742             type and refine its definition.
743              
744             Actually, you should use C<declare> instead; this is just an alias.
745              
746             This function is not exported by default.
747              
748             =item C<< type $name, %options >>
749              
750             =item C<< type %options >>
751              
752             Declare a named or anonymous type constraint which is not descended from
753             an existing type constraint. Use C<where> to provide a coderef that
754             constrains values.
755              
756             Actually, you should use C<declare> instead; this is just an alias.
757              
758             This function is not exported by default.
759              
760             =item C<< as $parent >>
761              
762             Used with C<declare> to specify a parent type constraint:
763              
764             declare EvenInt, as Int, where { $_ % 2 == 0 };
765              
766             =item C<< where { BLOCK } >>
767              
768             Used with C<declare> to provide the constraint coderef:
769              
770             declare EvenInt, as Int, where { $_ % 2 == 0 };
771              
772             The coderef operates on C<< $_ >>, which is the value being tested.
773              
774             =item C<< message { BLOCK } >>
775              
776             Generate a custom error message when a value fails validation.
777              
778             declare EvenInt,
779             as Int,
780             where { $_ % 2 == 0 },
781             message {
782             Int->validate($_) or "$_ is not divisible by two";
783             };
784              
785             Without a custom message, the messages generated by Type::Tiny are along
786             the lines of I<< Value "33" did not pass type constraint "EvenInt" >>,
787             which is usually reasonable.
788              
789             =item C<< inline_as { BLOCK } >>
790              
791             Generate a string of Perl code that can be used to inline the type check into
792             other functions. If your type check is being used within a L<Moose> or L<Moo>
793             constructor or accessor methods, or used by L<Type::Params>, this can lead to
794             significant performance improvements.
795              
796             declare EvenInt,
797             as Int,
798             where { $_ % 2 == 0 },
799             inline_as {
800             my ($constraint, $varname) = @_;
801             my $perlcode =
802             $constraint->parent->inline_check($varname)
803             . "&& ($varname % 2 == 0)";
804             return $perlcode;
805             };
806            
807             warn EvenInt->inline_check('$xxx'); # demonstration
808              
809             Your C<inline_as> block can return a list, in which case
810             these will be smushed together with "&&". The first item on the list may
811             be undef, in which case the undef will be replaced by the inlined parent
812             type constraint. (And will throw an exception if there is no parent.)
813              
814             declare EvenInt,
815             as Int,
816             where { $_ % 2 == 0 },
817             inline_as {
818             return (undef, "($_ % 2 == 0)");
819             };
820              
821             =item C<< class_type $name, { class => $package, %options } >>
822              
823             =item C<< class_type { class => $package, %options } >>
824              
825             =item C<< class_type $name >>
826              
827             Shortcut for declaring a L<Type::Tiny::Class> type constraint.
828              
829             If C<< $package >> is omitted, is assumed to be the same as C<< $name >>.
830             If C<< $name >> contains "::" (which would be an invalid name as far as
831             L<Type::Tiny> is concerned), this will be removed.
832              
833             So for example, C<< class_type("Foo::Bar") >> declares a L<Type::Tiny::Class>
834             type constraint named "FooBar" which constrains values to objects blessed
835             into the "Foo::Bar" package.
836              
837             =item C<< role_type $name, { role => $package, %options } >>
838              
839             =item C<< role_type { role => $package, %options } >>
840              
841             =item C<< role_type $name >>
842              
843             Shortcut for declaring a L<Type::Tiny::Role> type constraint.
844              
845             If C<< $package >> is omitted, is assumed to be the same as C<< $name >>.
846             If C<< $name >> contains "::" (which would be an invalid name as far as
847             L<Type::Tiny> is concerned), this will be removed.
848              
849             =item C<< duck_type $name, \@methods >>
850              
851             =item C<< duck_type \@methods >>
852              
853             Shortcut for declaring a L<Type::Tiny::Duck> type constraint.
854              
855             =item C<< union $name, \@constraints >>
856              
857             =item C<< union \@constraints >>
858              
859             Shortcut for declaring a L<Type::Tiny::Union> type constraint.
860              
861             =item C<< enum $name, \@values >>
862              
863             =item C<< enum \@values >>
864              
865             Shortcut for declaring a L<Type::Tiny::Enum> type constraint.
866              
867             =item C<< intersection $name, \@constraints >>
868              
869             =item C<< intersection \@constraints >>
870              
871             Shortcut for declaring a L<Type::Tiny::Intersection> type constraint.
872              
873             =back
874              
875             =head2 Coercion declaration functions
876              
877             Many of the following are similar to the similarly named functions described
878             in L<Moose::Util::TypeConstraints>.
879              
880             =over
881              
882             =item C<< coerce $target, @coercions >>
883              
884             Add coercions to the target type constraint. The list of coercions is a
885             list of type constraint, conversion code pairs. Conversion code can be
886             either a string of Perl code or a coderef; in either case the value to
887             be converted is C<< $_ >>.
888              
889             =item C<< from $source >>
890              
891             Sugar to specify a type constraint in a list of coercions:
892              
893             coerce EvenInt, from Int, via { $_ * 2 }; # As a coderef...
894             coerce EvenInt, from Int, q { $_ * 2 }; # or as a string!
895              
896             =item C<< via { BLOCK } >>
897              
898             Sugar to specify a coderef in a list of coercions.
899              
900             =item C<< declare_coercion $name, \%opts, $type1, $code1, ... >>
901              
902             =item C<< declare_coercion \%opts, $type1, $code1, ... >>
903              
904             Declares a coercion that is not explicitly attached to any type in the
905             library. For example:
906              
907             declare_coercion "ArrayRefFromAny", from "Any", via { [$_] };
908              
909             This coercion will be exportable from the library as a L<Type::Coercion>
910             object, but the ArrayRef type exported by the library won't automatically
911             use it.
912              
913             Coercions declared this way are immutable (frozen).
914              
915             =item C<< to_type $type >>
916              
917             Used with C<declare_coercion> to declare the target type constraint for
918             a coercion, but still without explicitly attaching the coercion to the
919             type constraint:
920              
921             declare_coercion "ArrayRefFromAny",
922             to_type "ArrayRef",
923             from "Any", via { [$_] };
924              
925             You should pretty much always use this when declaring an unattached
926             coercion because it's exceedingly useful for a type coercion to know what
927             it will coerce to - this allows it to skip coercion when no coercion is
928             needed (e.g. avoiding coercing C<< [] >> to C<< [ [] ] >>) and allows
929             C<assert_coerce> to work properly.
930              
931             =back
932              
933             =head2 Type library management
934              
935             =over
936              
937             =item C<< extends @libraries >>
938              
939             Indicates that this type library extends other type libraries, importing
940             their type constraints.
941              
942             Should usually be executed in a C<< BEGIN >> block.
943              
944             This is not exported by default because it's not fun to export it to Moo,
945             Moose or Mouse classes! C<< use Type::Utils -all >> can be used to import
946             it into your type library.
947              
948             =back
949              
950             =head2 Other
951              
952             =over
953              
954             =item C<< match_on_type $value => ($type => \&action, ..., \&default?) >>
955              
956             Something like a C<switch>/C<case> or C<given>/C<when> construct. Dispatches
957             along different code paths depending on the type of the incoming value.
958             Example blatantly stolen from the Moose documentation:
959              
960             sub to_json
961             {
962             my $value = shift;
963            
964             return match_on_type $value => (
965             HashRef() => sub {
966             my $hash = shift;
967             '{ '
968             . (
969             join ", " =>
970             map { '"' . $_ . '" : ' . to_json( $hash->{$_} ) }
971             sort keys %$hash
972             ) . ' }';
973             },
974             ArrayRef() => sub {
975             my $array = shift;
976             '[ '.( join ", " => map { to_json($_) } @$array ).' ]';
977             },
978             Num() => q {$_},
979             Str() => q { '"' . $_ . '"' },
980             Undef() => q {'null'},
981             => sub { die "$_ is not acceptable json type" },
982             );
983             }
984              
985             Note that unlike Moose, code can be specified as a string instead of a
986             coderef. (e.g. for C<Num>, C<Str> and C<Undef> above.)
987              
988             For improved performance, try C<compile_match_on_type>.
989              
990             This function is not exported by default.
991              
992             =item C<< my $coderef = compile_match_on_type($type => \&action, ..., \&default?) >>
993              
994             Compile a C<match_on_type> block into a coderef. The following JSON
995             converter is about two orders of magnitude faster than the previous
996             example:
997              
998             sub to_json;
999             *to_json = compile_match_on_type(
1000             HashRef() => sub {
1001             my $hash = shift;
1002             '{ '
1003             . (
1004             join ", " =>
1005             map { '"' . $_ . '" : ' . to_json( $hash->{$_} ) }
1006             sort keys %$hash
1007             ) . ' }';
1008             },
1009             ArrayRef() => sub {
1010             my $array = shift;
1011             '[ '.( join ", " => map { to_json($_) } @$array ).' ]';
1012             },
1013             Num() => q {$_},
1014             Str() => q { '"' . $_ . '"' },
1015             Undef() => q {'null'},
1016             => sub { die "$_ is not acceptable json type" },
1017             );
1018              
1019             Remember to store the coderef somewhere fairly permanent so that you
1020             don't compile it over and over. C<state> variables (in Perl >= 5.10)
1021             are good for this. (Same sort of idea as L<Type::Params>.)
1022              
1023             This function is not exported by default.
1024              
1025             =item C<< my $coderef = classifier(@types) >>
1026              
1027             Returns a coderef that can be used to classify values according to their
1028             type constraint. The coderef, when passed a value, returns a type
1029             constraint which the value satisfies.
1030              
1031             use feature qw( say );
1032             use Type::Utils qw( classifier );
1033             use Types::Standard qw( Int Num Str Any );
1034            
1035             my $classifier = classifier(Str, Int, Num, Any);
1036            
1037             say $classifier->( "42" )->name; # Int
1038             say $classifier->( "4.2" )->name; # Num
1039             say $classifier->( [] )->name; # Any
1040              
1041             Note that, for example, "42" satisfies Int, but it would satisfy the
1042             type constraints Num, Str, and Any as well. In this case, the
1043             classifier has picked the most specific type constraint that "42"
1044             satisfies.
1045              
1046             If no type constraint is satisfied by the value, then the classifier
1047             will return undef.
1048              
1049             =item C<< dwim_type($string, %options) >>
1050              
1051             Given a string like "ArrayRef[Int|CodeRef]", turns it into a type constraint
1052             object, hopefully doing what you mean.
1053              
1054             It uses the syntax of L<Type::Parser>. Firstly the L<Type::Registry>
1055             for the caller package is consulted; if that doesn't have a match,
1056             L<Types::Standard> is consulted for standard type constraint names.
1057              
1058             If none of the above yields a type constraint, and the caller class
1059             is a Moose-based class, then C<dwim_type> attempts to look the type
1060             constraint up in the Moose type registry. If it's a Mouse-based class,
1061             then the Mouse type registry is used instead.
1062              
1063             If no type constraint can be found via these normal methods, several
1064             fallbacks are available:
1065              
1066             =over
1067              
1068             =item C<lookup_via_moose>
1069              
1070             Lookup in Moose registry even if caller is non-Moose class.
1071              
1072             =item C<lookup_via_mouse>
1073              
1074             Lookup in Mouse registry even if caller is non-Mouse class.
1075              
1076             =item C<make_class_type>
1077              
1078             Create a new Type::Tiny::Class constraint.
1079              
1080             =item C<make_role_type>
1081              
1082             Create a new Type::Tiny::Role constraint.
1083              
1084             =back
1085              
1086             You can alter which should be attempted, and in which order, by passing
1087             an option to C<dwim_type>:
1088              
1089             my $type = Type::Utils::dwim_type(
1090             "ArrayRef[Int]",
1091             fallback => [ "lookup_via_mouse" , "make_role_type" ],
1092             );
1093              
1094             For historical reasons, by default the fallbacks attempted are:
1095              
1096             lookup_via_moose, lookup_via_mouse, make_class_type
1097              
1098             You may set C<fallback> to an empty arrayref to avoid using any of
1099             these fallbacks.
1100              
1101             You can specify an alternative for the caller using the C<for> option.
1102              
1103             my $type = dwim_type("ArrayRef", for => "Moose::Object");
1104              
1105             While it's probably better overall to use the proper L<Type::Registry>
1106             interface for resolving type constraint strings, this function often does
1107             what you want.
1108              
1109             It should never die if it fails to find a type constraint (but may die
1110             if the type constraint string is syntactically malformed), preferring to
1111             return undef.
1112              
1113             This function is not exported by default.
1114              
1115             =item C<< is($type, $value) >>
1116              
1117             Shortcut for C<< $type->check($value) >> but also if $type is a string,
1118             will look it up via C<dwim_type>.
1119              
1120             This function is not exported by default.
1121             This function is not even exported by C<< use Type::Utils -all >>.
1122             You must request it explicitly.
1123              
1124             use Type::Utils "is";
1125              
1126             Beware using this in test scripts because it has the same name as a function
1127             exported by L<Test::More>. Note that you can rename this function if
1128             C<is> will cause conflicts:
1129              
1130             use Type::Utils "is" => { -as => "isntnt" };
1131              
1132             =item C<< assert($type, $value) >>
1133              
1134             Like C<is> but instead of returning a boolean, returns C<< $value >> and
1135             dies if the value fails the type check.
1136              
1137             This function is not exported by default, but it is exported by
1138             C<< use Type::Utils -all >>.
1139              
1140             =item C<< english_list(\$conjunction, @items) >>
1141              
1142             Joins the items with commas, placing a conjunction before the final item.
1143             The conjunction is optional, defaulting to "and".
1144              
1145             english_list(qw/foo bar baz/); # "foo, bar, and baz"
1146             english_list(\"or", qw/quux quuux/); # "quux or quuux"
1147              
1148             This function is not exported by default.
1149              
1150             =back
1151              
1152             =head1 EXPORT
1153              
1154             By default, all of the functions documented above are exported, except
1155             C<subtype> and C<type> (prefer C<declare> instead), C<extends>, C<dwim_type>,
1156             C<match_on_type>/C<compile_match_on_type>, C<classifier>, and
1157             C<english_list>.
1158              
1159             This module uses L<Exporter::Tiny>; see the documentation of that module
1160             for tips and tricks importing from Type::Utils.
1161              
1162             =head1 BUGS
1163              
1164             Please report any bugs to
1165             L<https://github.com/tobyink/p5-type-tiny/issues>.
1166              
1167             =head1 SEE ALSO
1168              
1169             L<Type::Tiny::Manual>.
1170              
1171             L<Type::Tiny>, L<Type::Library>, L<Types::Standard>, L<Type::Coercion>.
1172              
1173             L<Type::Tiny::Class>, L<Type::Tiny::Role>, L<Type::Tiny::Duck>,
1174             L<Type::Tiny::Enum>, L<Type::Tiny::Union>.
1175              
1176             L<Moose::Util::TypeConstraints>,
1177             L<Mouse::Util::TypeConstraints>.
1178              
1179             =head1 AUTHOR
1180              
1181             Toby Inkster E<lt>tobyink@cpan.orgE<gt>.
1182              
1183             =head1 COPYRIGHT AND LICENCE
1184              
1185             This software is copyright (c) 2013-2014, 2017-2023 by Toby Inkster.
1186              
1187             This is free software; you can redistribute it and/or modify it under
1188             the same terms as the Perl 5 programming language system itself.
1189              
1190             =head1 DISCLAIMER OF WARRANTIES
1191              
1192             THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
1193             WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
1194             MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.