File Coverage

blib/lib/Types/Standard.pm
Criterion Covered Total %
statement 63 64 98.4
branch 9 14 64.2
condition 5 7 71.4
subroutine 19 19 100.0
pod 3 3 100.0
total 99 107 92.5


line stmt bran cond sub pod time code
1             package Types::Standard;
2              
3 253     253   1098253 use 5.008001;
  253         1133  
4 253     253   1378 use strict;
  253         553  
  253         5531  
5 253     253   1371 use warnings;
  253         587  
  253         17275  
6              
7             BEGIN {
8 253     253   849 eval { require re };
  253         1413  
9 253 50       10047 if ( $] < 5.010 ) { require Devel::TypeTiny::Perl58Compat }
  0         0  
10             }
11              
12             BEGIN {
13 253     253   907 $Types::Standard::AUTHORITY = 'cpan:TOBYINK';
14 253         11708 $Types::Standard::VERSION = '2.004000';
15             }
16              
17             $Types::Standard::VERSION =~ tr/_//d;
18              
19 253     253   76375 use Type::Library -base;
  253         701  
  253         2535  
20              
21             our @EXPORT_OK = qw( slurpy );
22              
23 253     253   14160 use Eval::TypeTiny qw( set_subname );
  253         617  
  253         1463  
24 253     253   99145 use Scalar::Util qw( blessed looks_like_number );
  253         565  
  253         13433  
25 253     253   1479 use Type::Tiny ();
  253         595  
  253         3413  
26 253     253   1203 use Types::TypeTiny ();
  253         503  
  253         29490  
27              
28             my $is_class_loaded;
29              
30             BEGIN {
31 253     253   925 $is_class_loaded = q{sub {
32             no strict 'refs';
33             return !!0 if ref $_[0];
34             return !!0 if not $_[0];
35             return !!0 if ref(do { my $tmpstr = $_[0]; \$tmpstr }) ne 'SCALAR';
36             my $stash = \%{"$_[0]\::"};
37             return !!1 if exists($stash->{'ISA'}) && *{$stash->{'ISA'}}{ARRAY} && @{$_[0].'::ISA'};
38             return !!1 if exists($stash->{'VERSION'});
39             foreach my $globref (values %$stash) {
40             return !!1
41             if ref \$globref eq 'GLOB'
42             ? *{$globref}{CODE}
43             : ref $globref; # const or sub ref
44             }
45             return !!0;
46             }};
47            
48 253         759 *_is_class_loaded =
49             Type::Tiny::_USE_XS
50             ? \&Type::Tiny::XS::Util::is_class_loaded
51             : eval $is_class_loaded;
52            
53             *_HAS_REFUTILXS = eval {
54 253         117787 require Ref::Util::XS;
55 253         170433 Ref::Util::XS::->VERSION( 0.100 );
56 253         220328 1;
57             }
58             ? sub () { !!1 }
59 253 50       547 : sub () { !!0 };
60             } #/ BEGIN
61              
62             my $add_core_type = sub {
63             my $meta = shift;
64             my ( $typedef ) = @_;
65            
66             my $name = $typedef->{name};
67             my ( $xsub, $xsubname );
68            
69             # We want Map and Tuple to be XSified, even if they're not
70             # really core.
71             $typedef->{_is_core} = 1
72             unless $name eq 'Map' || $name eq 'Tuple';
73            
74             if ( Type::Tiny::_USE_XS
75             and not( $name eq 'RegexpRef' ) )
76             {
77             $xsub = Type::Tiny::XS::get_coderef_for( $name );
78             $xsubname = Type::Tiny::XS::get_subname_for( $name );
79             }
80            
81             elsif ( Type::Tiny::_USE_MOUSE
82             and not( $name eq 'RegexpRef' or $name eq 'Int' or $name eq 'Object' ) )
83             {
84             require Mouse::Util::TypeConstraints;
85             $xsub = "Mouse::Util::TypeConstraints"->can( $name );
86             $xsubname = "Mouse::Util::TypeConstraints::$name" if $xsub;
87             }
88            
89             if ( Type::Tiny::_USE_XS
90             and Type::Tiny::XS->VERSION < 0.014
91             and $name eq 'Bool' )
92             {
93             # Broken implementation of Bool
94             $xsub = $xsubname = undef;
95             }
96            
97             if ( Type::Tiny::_USE_XS
98             and ( Type::Tiny::XS->VERSION < 0.016 or $] < 5.018 )
99             and $name eq 'Int' )
100             {
101             # Broken implementation of Int
102             $xsub = $xsubname = undef;
103             }
104            
105             $typedef->{compiled_type_constraint} = $xsub if $xsub;
106            
107             my $orig_inlined = $typedef->{inlined};
108             if (
109             defined( $xsubname ) and (
110            
111             # These should be faster than their normal inlined
112             # equivalents
113             $name eq 'Str'
114             or $name eq 'Bool'
115             or $name eq 'ClassName'
116             or $name eq 'RegexpRef'
117             or $name eq 'FileHandle'
118             )
119             )
120             {
121             $typedef->{inlined} = sub {
122             $Type::Tiny::AvoidCallbacks ? goto( $orig_inlined ) : "$xsubname\($_[1])";
123             };
124             } #/ if ( defined( $xsubname...))
125            
126             @_ = ( $meta, $typedef );
127             goto \&Type::Library::add_type;
128             };
129              
130             my $maybe_load_modules = sub {
131             my $code = pop;
132             if ( $Type::Tiny::AvoidCallbacks ) {
133             $code = sprintf(
134             'do { %s %s; %s }',
135             $Type::Tiny::SafePackage,
136             join( '; ', map "use $_ ()", @_ ),
137             $code,
138             );
139             }
140             $code;
141             };
142              
143 3     3   30 sub _croak ($;@) { require Error::TypeTiny; goto \&Error::TypeTiny::croak }
  3         15  
144              
145             my $meta = __PACKAGE__->meta;
146              
147             # Stringable and LazyLoad are optimizations that complicate
148             # this module somewhat, but they have led to performance
149             # improvements. If Types::Standard wasn't such a key type
150             # library, I wouldn't use them. I strongly discourage anybody
151             # from using them in their own code. If you're looking for
152             # examples of how to write a type library sanely, you're
153             # better off looking at the code for Types::Common::Numeric
154             # and Types::Common::String.
155              
156             {
157              
158             sub Stringable (&) {
159 42     42 1 415 bless +{ code => $_[0] }, 'Types::Standard::_Stringable';
160             }
161             Types::Standard::_Stringable->Type::Tiny::_install_overloads(
162 71   66 71   539 q[""] => sub { $_[0]{text} ||= $_[0]{code}->() } );
163            
164             sub LazyLoad ($$) {
165 9867     9867 1 54877 bless \@_, 'Types::Standard::LazyLoad';
166             }
167             'Types::Standard::LazyLoad'->Type::Tiny::_install_overloads(
168             q[&{}] => sub {
169 162     162   459 my ( $typename, $function ) = @{ $_[0] };
  162         771  
170 162         1341 my $type = $meta->get_type( $typename );
171 162         615 my $class = "Types::Standard::$typename";
172 162 50       11799 eval "require $class; 1" or die( $@ );
173            
174             # Majorly break encapsulation for Type::Tiny :-O
175 162         1965 for my $key ( keys %$type ) {
176 3233 100       8513 next unless ref( $type->{$key} ) eq 'Types::Standard::LazyLoad';
177 620         1578 my $f = $type->{$key}[1];
178 620         5001 $type->{$key} = $class->can( "__$f" );
179             }
180 162   100     1349 my $mm = $type->{my_methods} || {};
181 162         734 for my $key ( keys %$mm ) {
182 117 50       430 next unless ref( $mm->{$key} ) eq 'Types::Standard::LazyLoad';
183 117         289 my $f = $mm->{$key}[1];
184 117         617 $mm->{$key} = $class->can( "__$f" );
185             set_subname(
186             sprintf( "%s::my_%s", $type->qualified_name, $key ),
187 117         471 $mm->{$key},
188             );
189             } #/ for my $key ( keys %$mm)
190 162         1425 return $class->can( "__$function" );
191             },
192             );
193             }
194              
195 253     253   2245 no warnings;
  253         619  
  253         23589  
196              
197             BEGIN {
198             *STRICTNUM =
199             $ENV{PERL_TYPES_STANDARD_STRICTNUM}
200             ? sub() { !!1 }
201             : sub() { !!0 }
202 253 50   253   1655846 }
203              
204             my $_any = $meta->$add_core_type(
205             {
206             name => "Any",
207             inlined => sub { "!!1" },
208             complement_name => 'None',
209             type_default => sub { return undef; },
210             }
211             );
212              
213             my $_item = $meta->$add_core_type(
214             {
215             name => "Item",
216             inlined => sub { "!!1" },
217             parent => $_any,
218             }
219             );
220              
221             my $_bool = $meta->$add_core_type(
222             {
223             name => "Bool",
224             parent => $_item,
225             constraint => sub {
226             !ref $_ and ( !defined $_ or $_ eq q() or $_ eq '0' or $_ eq '1' );
227             },
228             inlined => sub {
229             "!ref $_[1] and (!defined $_[1] or $_[1] eq q() or $_[1] eq '0' or $_[1] eq '1')";
230             },
231             type_default => sub { return !!0; },
232             }
233             );
234              
235             $_bool->coercion->add_type_coercions( $_any, q{!!$_} );
236              
237             my $_undef = $meta->$add_core_type(
238             {
239             name => "Undef",
240             parent => $_item,
241             constraint => sub { !defined $_ },
242             inlined => sub { "!defined($_[1])" },
243             type_default => sub { return undef; },
244             }
245             );
246              
247             my $_def = $meta->$add_core_type(
248             {
249             name => "Defined",
250             parent => $_item,
251             constraint => sub { defined $_ },
252             inlined => sub { "defined($_[1])" },
253             complementary_type => $_undef,
254             }
255             );
256              
257             # hackish, but eh
258             Scalar::Util::weaken( $_undef->{complementary_type} ||= $_def );
259              
260             my $_val = $meta->$add_core_type(
261             {
262             name => "Value",
263             parent => $_def,
264             constraint => sub { not ref $_ },
265             inlined => sub { "defined($_[1]) and not ref($_[1])" },
266             }
267             );
268              
269             my $_str = $meta->$add_core_type(
270             {
271             name => "Str",
272             parent => $_val,
273             constraint => sub {
274             ref( \$_ ) eq 'SCALAR' or ref( \( my $val = $_ ) ) eq 'SCALAR';
275             },
276             inlined => sub {
277             "defined($_[1]) and do { ref(\\$_[1]) eq 'SCALAR' or ref(\\(my \$val = $_[1])) eq 'SCALAR' }";
278             },
279             sorter => sub { $_[0] cmp $_[1] },
280             type_default => sub { return ''; },
281             }
282             );
283              
284             my $_laxnum = $meta->add_type(
285             {
286             name => "LaxNum",
287             parent => $_str,
288             constraint => sub { looks_like_number( $_ ) and ref( \$_ ) ne 'GLOB' },
289             inlined => sub {
290             $maybe_load_modules->(
291             qw/ Scalar::Util /,
292             'Scalar::Util'->VERSION ge '1.18' # RT 132426
293             ? "defined($_[1]) && !ref($_[1]) && Scalar::Util::looks_like_number($_[1])"
294             : "defined($_[1]) && !ref($_[1]) && Scalar::Util::looks_like_number($_[1]) && ref(\\($_[1])) ne 'GLOB'"
295             );
296             },
297             sorter => sub { $_[0] <=> $_[1] },
298             type_default => sub { return 0; },
299             }
300             );
301              
302             my $_strictnum = $meta->add_type(
303             {
304             name => "StrictNum",
305             parent => $_str,
306             constraint => sub {
307             my $val = $_;
308             ( $val =~ /\A[+-]?[0-9]+\z/ )
309             || (
310             $val =~ /\A(?:[+-]?) #matches optional +- in the beginning
311             (?=[0-9]|\.[0-9]) #matches previous +- only if there is something like 3 or .3
312             [0-9]* #matches 0-9 zero or more times
313             (?:\.[0-9]+)? #matches optional .89 or nothing
314             (?:[Ee](?:[+-]?[0-9]+))? #matches E1 or e1 or e-1 or e+1 etc
315             \z/x
316             );
317             },
318             inlined => sub {
319             'my $val = '
320             . $_[1] . ';'
321             . Value()->inline_check( '$val' )
322             . ' && ( $val =~ /\A[+-]?[0-9]+\z/ || '
323             . '$val =~ /\A(?:[+-]?) # matches optional +- in the beginning
324             (?=[0-9]|\.[0-9]) # matches previous +- only if there is something like 3 or .3
325             [0-9]* # matches 0-9 zero or more times
326             (?:\.[0-9]+)? # matches optional .89 or nothing
327             (?:[Ee](?:[+-]?[0-9]+))? # matches E1 or e1 or e-1 or e+1 etc
328             \z/x ); '
329             },
330             sorter => sub { $_[0] <=> $_[1] },
331             type_default => sub { return 0; },
332             }
333             );
334              
335             my $_num = $meta->add_type(
336             {
337             name => "Num",
338             parent => ( STRICTNUM ? $_strictnum : $_laxnum ),
339             }
340             );
341              
342             $meta->$add_core_type(
343             {
344             name => "Int",
345             parent => $_num,
346             constraint => sub { /\A-?[0-9]+\z/ },
347             inlined => sub {
348             "do { my \$tmp = $_[1]; defined(\$tmp) and !ref(\$tmp) and \$tmp =~ /\\A-?[0-9]+\\z/ }";
349             },
350             type_default => sub { return 0; },
351             }
352             );
353              
354             my $_classn = $meta->add_type(
355             {
356             name => "ClassName",
357             parent => $_str,
358             constraint => \&_is_class_loaded,
359             inlined => sub {
360             $Type::Tiny::AvoidCallbacks
361             ? "($is_class_loaded)->(do { my \$tmp = $_[1] })"
362             : "Types::Standard::_is_class_loaded(do { my \$tmp = $_[1] })";
363             },
364             }
365             );
366              
367             $meta->add_type(
368             {
369             name => "RoleName",
370             parent => $_classn,
371             constraint => sub { not $_->can( "new" ) },
372             inlined => sub {
373             $Type::Tiny::AvoidCallbacks
374             ? "($is_class_loaded)->(do { my \$tmp = $_[1] }) and not $_[1]\->can('new')"
375             : "Types::Standard::_is_class_loaded(do { my \$tmp = $_[1] }) and not $_[1]\->can('new')";
376             },
377             }
378             );
379              
380             my $_ref = $meta->$add_core_type(
381             {
382             name => "Ref",
383             parent => $_def,
384             constraint => sub { ref $_ },
385             inlined => sub { "!!ref($_[1])" },
386             constraint_generator => sub {
387             return $meta->get_type( 'Ref' ) unless @_;
388            
389             my $reftype = shift;
390             $reftype =~
391             /^(SCALAR|ARRAY|HASH|CODE|REF|GLOB|LVALUE|FORMAT|IO|VSTRING|REGEXP|Regexp)$/i
392             or _croak(
393             "Parameter to Ref[`a] expected to be a Perl ref type; got $reftype" );
394            
395             $reftype = "$reftype";
396             return sub {
397             ref( $_[0] ) and Scalar::Util::reftype( $_[0] ) eq $reftype;
398             }
399             },
400             inline_generator => sub {
401             my $reftype = shift;
402             return sub {
403             my $v = $_[1];
404             $maybe_load_modules->(
405             qw/ Scalar::Util /,
406             "ref($v) and Scalar::Util::reftype($v) eq q($reftype)"
407             );
408             };
409             },
410             deep_explanation => sub {
411             require B;
412             my ( $type, $value, $varname ) = @_;
413             my $param = $type->parameters->[0];
414             return if $type->check( $value );
415             my $reftype = Scalar::Util::reftype( $value );
416             return [
417             sprintf(
418             '"%s" constrains reftype(%s) to be equal to %s', $type, $varname,
419             B::perlstring( $param )
420             ),
421             sprintf(
422             'reftype(%s) is %s', $varname,
423             defined( $reftype ) ? B::perlstring( $reftype ) : "undef"
424             ),
425             ];
426             },
427             }
428             );
429              
430             $meta->$add_core_type(
431             {
432             name => "CodeRef",
433             parent => $_ref,
434             constraint => sub { ref $_ eq "CODE" },
435             inlined => sub {
436             _HAS_REFUTILXS && !$Type::Tiny::AvoidCallbacks
437             ? "Ref::Util::XS::is_plain_coderef($_[1])"
438             : "ref($_[1]) eq 'CODE'";
439             },
440             type_default => sub { return sub {}; },
441             }
442             );
443              
444             my $_regexp = $meta->$add_core_type(
445             {
446             name => "RegexpRef",
447             parent => $_ref,
448             constraint => sub {
449             ref( $_ ) && !!re::is_regexp( $_ ) or blessed( $_ ) && $_->isa( 'Regexp' );
450             },
451             inlined => sub {
452             my $v = $_[1];
453             $maybe_load_modules->(
454             qw/ Scalar::Util re /,
455             "ref($v) && !!re::is_regexp($v) or Scalar::Util::blessed($v) && $v\->isa('Regexp')"
456             );
457             },
458             type_default => sub { return qr//; },
459             }
460             );
461              
462             $meta->$add_core_type(
463             {
464             name => "GlobRef",
465             parent => $_ref,
466             constraint => sub { ref $_ eq "GLOB" },
467             inlined => sub {
468             _HAS_REFUTILXS && !$Type::Tiny::AvoidCallbacks
469             ? "Ref::Util::XS::is_plain_globref($_[1])"
470             : "ref($_[1]) eq 'GLOB'";
471             },
472             }
473             );
474              
475             $meta->$add_core_type(
476             {
477             name => "FileHandle",
478             parent => $_ref,
479             constraint => sub {
480             ( ref( $_ ) && Scalar::Util::openhandle( $_ ) )
481             or ( blessed( $_ ) && $_->isa( "IO::Handle" ) );
482             },
483             inlined => sub {
484             $maybe_load_modules->(
485             qw/ Scalar::Util /,
486             "(ref($_[1]) && Scalar::Util::openhandle($_[1])) "
487             . "or (Scalar::Util::blessed($_[1]) && $_[1]\->isa(\"IO::Handle\"))"
488             );
489             },
490             }
491             );
492              
493             my $_arr = $meta->$add_core_type(
494             {
495             name => "ArrayRef",
496             parent => $_ref,
497             constraint => sub { ref $_ eq "ARRAY" },
498             inlined => sub {
499             _HAS_REFUTILXS && !$Type::Tiny::AvoidCallbacks
500             ? "Ref::Util::XS::is_plain_arrayref($_[1])"
501             : "ref($_[1]) eq 'ARRAY'";
502             },
503             constraint_generator => LazyLoad( ArrayRef => 'constraint_generator' ),
504             inline_generator => LazyLoad( ArrayRef => 'inline_generator' ),
505             deep_explanation => LazyLoad( ArrayRef => 'deep_explanation' ),
506             coercion_generator => LazyLoad( ArrayRef => 'coercion_generator' ),
507             type_default => sub { return []; },
508             type_default_generator => sub {
509             return $Type::Tiny::parameterize_type->type_default if @_ < 2;
510             return undef;
511             },
512             }
513             );
514              
515             my $_hash = $meta->$add_core_type(
516             {
517             name => "HashRef",
518             parent => $_ref,
519             constraint => sub { ref $_ eq "HASH" },
520             inlined => sub {
521             _HAS_REFUTILXS && !$Type::Tiny::AvoidCallbacks
522             ? "Ref::Util::XS::is_plain_hashref($_[1])"
523             : "ref($_[1]) eq 'HASH'";
524             },
525             constraint_generator => LazyLoad( HashRef => 'constraint_generator' ),
526             inline_generator => LazyLoad( HashRef => 'inline_generator' ),
527             deep_explanation => LazyLoad( HashRef => 'deep_explanation' ),
528             coercion_generator => LazyLoad( HashRef => 'coercion_generator' ),
529             type_default => sub { return {}; },
530             type_default_generator => sub {
531             return $Type::Tiny::parameterize_type->type_default if @_ < 2;
532             return undef;
533             },
534             my_methods => {
535             hashref_allows_key => LazyLoad( HashRef => 'hashref_allows_key' ),
536             hashref_allows_value => LazyLoad( HashRef => 'hashref_allows_value' ),
537             },
538             }
539             );
540              
541             $meta->$add_core_type(
542             {
543             name => "ScalarRef",
544             parent => $_ref,
545             constraint => sub { ref $_ eq "SCALAR" or ref $_ eq "REF" },
546             inlined => sub { "ref($_[1]) eq 'SCALAR' or ref($_[1]) eq 'REF'" },
547             constraint_generator => LazyLoad( ScalarRef => 'constraint_generator' ),
548             inline_generator => LazyLoad( ScalarRef => 'inline_generator' ),
549             deep_explanation => LazyLoad( ScalarRef => 'deep_explanation' ),
550             coercion_generator => LazyLoad( ScalarRef => 'coercion_generator' ),
551             type_default => sub { my $x; return \$x; },
552             }
553             );
554              
555             my $_obj = $meta->$add_core_type(
556             {
557             name => "Object",
558             parent => $_ref,
559             constraint => sub { blessed $_ },
560             inlined => sub {
561             _HAS_REFUTILXS && !$Type::Tiny::AvoidCallbacks
562             ? "Ref::Util::XS::is_blessed_ref($_[1])"
563             : $maybe_load_modules->(
564             'Scalar::Util',
565             "Scalar::Util::blessed($_[1])"
566             );
567             },
568             is_object => 1,
569             }
570             );
571              
572             $meta->$add_core_type(
573             {
574             name => "Maybe",
575             parent => $_item,
576             constraint_generator => sub {
577             return $meta->get_type( 'Maybe' ) unless @_;
578            
579             my $param = Types::TypeTiny::to_TypeTiny( shift );
580             Types::TypeTiny::is_TypeTiny( $param )
581             or _croak(
582             "Parameter to Maybe[`a] expected to be a type constraint; got $param" );
583            
584             my $param_compiled_check = $param->compiled_check;
585             my @xsub;
586             if ( Type::Tiny::_USE_XS ) {
587             my $paramname = Type::Tiny::XS::is_known( $param_compiled_check );
588             push @xsub, Type::Tiny::XS::get_coderef_for( "Maybe[$paramname]" )
589             if $paramname;
590             }
591             elsif ( Type::Tiny::_USE_MOUSE and $param->_has_xsub ) {
592             require Mouse::Util::TypeConstraints;
593             my $maker = "Mouse::Util::TypeConstraints"->can( "_parameterize_Maybe_for" );
594             push @xsub, $maker->( $param ) if $maker;
595             }
596            
597             return (
598             sub {
599             my $value = shift;
600             return !!1 unless defined $value;
601             return $param->check( $value );
602             },
603             @xsub,
604             );
605             },
606             inline_generator => sub {
607             my $param = shift;
608            
609             my $param_compiled_check = $param->compiled_check;
610             my $xsubname;
611             if ( Type::Tiny::_USE_XS ) {
612             my $paramname = Type::Tiny::XS::is_known( $param_compiled_check );
613             $xsubname = Type::Tiny::XS::get_subname_for( "Maybe[$paramname]" );
614             }
615            
616             return unless $param->can_be_inlined;
617             return sub {
618             my $v = $_[1];
619             return "$xsubname\($v\)" if $xsubname && !$Type::Tiny::AvoidCallbacks;
620             my $param_check = $param->inline_check( $v );
621             "!defined($v) or $param_check";
622             };
623             },
624             deep_explanation => sub {
625             my ( $type, $value, $varname ) = @_;
626             my $param = $type->parameters->[0];
627            
628             return [
629             sprintf( '%s is defined', Type::Tiny::_dd( $value ) ),
630             sprintf(
631             '"%s" constrains the value with "%s" if it is defined', $type, $param
632             ),
633             @{ $param->validate_explain( $value, $varname ) },
634             ];
635             },
636             coercion_generator => sub {
637             my ( $parent, $child, $param ) = @_;
638             return unless $param->has_coercion;
639             return $param->coercion;
640             },
641             type_default => sub { return undef; },
642             type_default_generator => sub {
643             $_[0]->type_default || $Type::Tiny::parameterize_type->type_default ;
644             },
645             }
646             );
647              
648             my $_map = $meta->$add_core_type(
649             {
650             name => "Map",
651             parent => $_hash,
652             constraint_generator => LazyLoad( Map => 'constraint_generator' ),
653             inline_generator => LazyLoad( Map => 'inline_generator' ),
654             deep_explanation => LazyLoad( Map => 'deep_explanation' ),
655             coercion_generator => LazyLoad( Map => 'coercion_generator' ),
656             my_methods => {
657             hashref_allows_key => LazyLoad( Map => 'hashref_allows_key' ),
658             hashref_allows_value => LazyLoad( Map => 'hashref_allows_value' ),
659             },
660             type_default_generator => sub {
661             return $Type::Tiny::parameterize_type->type_default;
662             },
663             }
664             );
665              
666             my $_Optional = $meta->add_type(
667             {
668             name => "Optional",
669             parent => $_item,
670             constraint_generator => sub {
671             return $meta->get_type( 'Optional' ) unless @_;
672            
673             my $param = Types::TypeTiny::to_TypeTiny( shift );
674             Types::TypeTiny::is_TypeTiny( $param )
675             or _croak(
676             "Parameter to Optional[`a] expected to be a type constraint; got $param" );
677            
678             sub { $param->check( $_[0] ) }
679             },
680             inline_generator => sub {
681             my $param = shift;
682             return unless $param->can_be_inlined;
683             return sub {
684             my $v = $_[1];
685             $param->inline_check( $v );
686             };
687             },
688             deep_explanation => sub {
689             my ( $type, $value, $varname ) = @_;
690             my $param = $type->parameters->[0];
691            
692             return [
693             sprintf( '%s exists', $varname ),
694             sprintf( '"%s" constrains %s with "%s" if it exists', $type, $varname, $param ),
695             @{ $param->validate_explain( $value, $varname ) },
696             ];
697             },
698             coercion_generator => sub {
699             my ( $parent, $child, $param ) = @_;
700             return unless $param->has_coercion;
701             return $param->coercion;
702             },
703             type_default_generator => sub {
704             return $_[0]->type_default;
705             },
706             }
707             );
708              
709             my $_slurpy;
710             $_slurpy = $meta->add_type(
711             {
712             name => "Slurpy",
713             slurpy => 1,
714             parent => $_item,
715             constraint_generator => sub {
716             my $self = $_slurpy;
717             my $param = @_ ? Types::TypeTiny::to_TypeTiny(shift) : $_any;
718             Types::TypeTiny::is_TypeTiny( $param )
719             or _croak(
720             "Parameter to Slurpy[`a] expected to be a type constraint; got $param" );
721            
722             return $self->create_child_type(
723             slurpy => 1,
724             display_name => $self->name_generator->( $self, $param ),
725             parameters => [ $param ],
726             constraint => sub { $param->check( $_[0] ) },
727             type_default => $param->type_default,
728             _build_coercion => sub {
729             my $coercion = shift;
730             $coercion->add_type_coercions( @{ $param->coercion->type_coercion_map } )
731             if $param->has_coercion;
732             $coercion->freeze;
733             },
734             $param->can_be_inlined
735             ? ( inlined => sub { $param->inline_check( $_[1] ) } )
736             : (),
737             );
738             },
739             deep_explanation => sub {
740             my ( $type, $value, $varname ) = @_;
741             my $param = $type->parameters->[0];
742             return [
743             sprintf( '%s is slurpy', $varname ),
744             @{ $param->validate_explain( $value, $varname ) },
745             ];
746             },
747             my_methods => {
748             'unslurpy' => sub {
749             my $self = shift;
750             $self->{_my_unslurpy} ||= $self->find_parent(
751             sub { $_->parent->{uniq} == $_slurpy->{uniq} }
752             )->type_parameter;
753             },
754             'slurp_into' => sub {
755             my $self = shift;
756             my $parameters = $self->find_parent(
757             sub { $_->parent->{uniq} == $_slurpy->{uniq} }
758             )->parameters;
759             if ( $parameters->[1] ) {
760             return $parameters->[1];
761             }
762             my $constraint = $parameters->[0];
763             return 'HASH'
764             if $constraint->is_a_type_of( HashRef() )
765             or $constraint->is_a_type_of( Map() )
766             or $constraint->is_a_type_of( Dict() );
767             return 'ARRAY';
768             },
769             },
770             }
771             );
772              
773             sub slurpy {
774 97     97 1 4138 my $t = shift;
775 97         379 my $s = $_slurpy->of( $t );
776 95   50     359 $s->{slurpy} ||= 1;
777 95 100       761 wantarray ? ( $s, @_ ) : $s;
778             }
779              
780             $meta->$add_core_type(
781             {
782             name => "Tuple",
783             parent => $_arr,
784             name_generator => sub {
785             my ( $s, @a ) = @_;
786             sprintf( '%s[%s]', $s, join q[,], @a );
787             },
788             constraint_generator => LazyLoad( Tuple => 'constraint_generator' ),
789             inline_generator => LazyLoad( Tuple => 'inline_generator' ),
790             deep_explanation => LazyLoad( Tuple => 'deep_explanation' ),
791             coercion_generator => LazyLoad( Tuple => 'coercion_generator' ),
792             }
793             );
794              
795             $meta->add_type(
796             {
797             name => "CycleTuple",
798             parent => $_arr,
799             name_generator => sub {
800             my ( $s, @a ) = @_;
801             sprintf( '%s[%s]', $s, join q[,], @a );
802             },
803             constraint_generator => LazyLoad( CycleTuple => 'constraint_generator' ),
804             inline_generator => LazyLoad( CycleTuple => 'inline_generator' ),
805             deep_explanation => LazyLoad( CycleTuple => 'deep_explanation' ),
806             coercion_generator => LazyLoad( CycleTuple => 'coercion_generator' ),
807             }
808             );
809              
810             $meta->add_type(
811             {
812             name => "Dict",
813             parent => $_hash,
814             name_generator => sub {
815             my ( $s, @p ) = @_;
816             my $l = @p
817             && Types::TypeTiny::is_TypeTiny( $p[-1] )
818             && $p[-1]->is_strictly_a_type_of( Types::Standard::Slurpy() )
819             ? pop(@p)
820             : undef;
821             my %a = @p;
822             sprintf(
823             '%s[%s%s]', $s,
824             join( q[,], map sprintf( "%s=>%s", $_, $a{$_} ), sort keys %a ),
825             $l ? ",$l" : ''
826             );
827             },
828             constraint_generator => LazyLoad( Dict => 'constraint_generator' ),
829             inline_generator => LazyLoad( Dict => 'inline_generator' ),
830             deep_explanation => LazyLoad( Dict => 'deep_explanation' ),
831             coercion_generator => LazyLoad( Dict => 'coercion_generator' ),
832             my_methods => {
833             dict_is_slurpy => LazyLoad( Dict => 'dict_is_slurpy' ),
834             hashref_allows_key => LazyLoad( Dict => 'hashref_allows_key' ),
835             hashref_allows_value => LazyLoad( Dict => 'hashref_allows_value' ),
836             },
837             }
838             );
839              
840             $meta->add_type(
841             {
842             name => "Overload",
843             parent => $_obj,
844             constraint => sub { require overload; overload::Overloaded( $_ ) },
845             inlined => sub {
846             $maybe_load_modules->(
847             qw/ Scalar::Util overload /,
848             $INC{'overload.pm'}
849             ? "Scalar::Util::blessed($_[1]) and overload::Overloaded($_[1])"
850             : "Scalar::Util::blessed($_[1]) and do { use overload (); overload::Overloaded($_[1]) }"
851             );
852             },
853             constraint_generator => sub {
854             return $meta->get_type( 'Overload' ) unless @_;
855            
856             my @operations = map {
857             Types::TypeTiny::is_StringLike( $_ )
858             ? "$_"
859             : _croak( "Parameters to Overload[`a] expected to be a strings; got $_" );
860             } @_;
861            
862             require overload;
863             return sub {
864             my $value = shift;
865             for my $op ( @operations ) {
866             return unless overload::Method( $value, $op );
867             }
868             return !!1;
869             }
870             },
871             inline_generator => sub {
872             my @operations = @_;
873             return sub {
874             require overload;
875             my $v = $_[1];
876             $maybe_load_modules->(
877             qw/ Scalar::Util overload /,
878             join " and ",
879             "Scalar::Util::blessed($v)",
880             map "overload::Method($v, q[$_])", @operations
881             );
882             };
883             },
884             is_object => 1,
885             }
886             );
887              
888             $meta->add_type(
889             {
890             name => "StrMatch",
891             parent => $_str,
892             constraint_generator => LazyLoad( StrMatch => 'constraint_generator' ),
893             inline_generator => LazyLoad( StrMatch => 'inline_generator' ),
894             }
895             );
896              
897             $meta->add_type(
898             {
899             name => "OptList",
900             parent => $_arr,
901             constraint => sub {
902             for my $inner ( @$_ ) {
903             return unless ref( $inner ) eq q(ARRAY);
904             return unless @$inner == 2;
905             return unless is_Str( $inner->[0] );
906             }
907             return !!1;
908             },
909             inlined => sub {
910             my ( $self, $var ) = @_;
911             my $Str_check = Str()->inline_check( '$inner->[0]' );
912             my @code = 'do { my $ok = 1; ';
913             push @code, sprintf( 'for my $inner (@{%s}) { no warnings; ', $var );
914             push @code,
915             sprintf(
916             '($ok=0) && last unless ref($inner) eq q(ARRAY) && @$inner == 2 && (%s); ',
917             $Str_check
918             );
919             push @code, '} ';
920             push @code, '$ok }';
921             return ( undef, join( q( ), @code ) );
922             },
923             type_default => sub { return [] },
924             }
925             );
926              
927             $meta->add_type(
928             {
929             name => "Tied",
930             parent => $_ref,
931             constraint => sub {
932             !!tied(
933             Scalar::Util::reftype( $_ ) eq 'HASH' ? %{$_}
934             : Scalar::Util::reftype( $_ ) eq 'ARRAY' ? @{$_}
935             : Scalar::Util::reftype( $_ ) =~ /^(SCALAR|REF)$/ ? ${$_}
936             : undef
937             );
938             },
939             inlined => sub {
940             my ( $self, $var ) = @_;
941             $maybe_load_modules->(
942             qw/ Scalar::Util /,
943             $self->parent->inline_check( $var )
944             . " and !!tied(Scalar::Util::reftype($var) eq 'HASH' ? \%{$var} : Scalar::Util::reftype($var) eq 'ARRAY' ? \@{$var} : Scalar::Util::reftype($var) =~ /^(SCALAR|REF)\$/ ? \${$var} : undef)"
945             );
946             },
947             name_generator => sub {
948             my $self = shift;
949             my $param = Types::TypeTiny::to_TypeTiny( shift );
950             unless ( Types::TypeTiny::is_TypeTiny( $param ) ) {
951             Types::TypeTiny::is_StringLike( $param )
952             or _croak( "Parameter to Tied[`a] expected to be a class name; got $param" );
953             require B;
954             return sprintf( "%s[%s]", $self, B::perlstring( $param ) );
955             }
956             return sprintf( "%s[%s]", $self, $param );
957             },
958             constraint_generator => LazyLoad( Tied => 'constraint_generator' ),
959             inline_generator => LazyLoad( Tied => 'inline_generator' ),
960             }
961             );
962              
963             $meta->add_type(
964             {
965             name => "InstanceOf",
966             parent => $_obj,
967             constraint_generator => sub {
968             return $meta->get_type( 'InstanceOf' ) unless @_;
969             require Type::Tiny::Class;
970             my @classes = map {
971             Types::TypeTiny::is_TypeTiny( $_ )
972             ? $_
973             : "Type::Tiny::Class"->new(
974             class => $_,
975             display_name => sprintf( 'InstanceOf[%s]', B::perlstring( $_ ) )
976             )
977             } @_;
978             return $classes[0] if @classes == 1;
979            
980             require B;
981             require Type::Tiny::Union;
982             return "Type::Tiny::Union"->new(
983             type_constraints => \@classes,
984             display_name => sprintf(
985             'InstanceOf[%s]', join q[,], map B::perlstring( $_->class ), @classes
986             ),
987             );
988             },
989             }
990             );
991              
992             $meta->add_type(
993             {
994             name => "ConsumerOf",
995             parent => $_obj,
996             constraint_generator => sub {
997             return $meta->get_type( 'ConsumerOf' ) unless @_;
998             require B;
999             require Type::Tiny::Role;
1000             my @roles = map {
1001             Types::TypeTiny::is_TypeTiny( $_ )
1002             ? $_
1003             : "Type::Tiny::Role"->new(
1004             role => $_,
1005             display_name => sprintf( 'ConsumerOf[%s]', B::perlstring( $_ ) )
1006             )
1007             } @_;
1008             return $roles[0] if @roles == 1;
1009            
1010             require Type::Tiny::Intersection;
1011             return "Type::Tiny::Intersection"->new(
1012             type_constraints => \@roles,
1013             display_name => sprintf(
1014             'ConsumerOf[%s]', join q[,], map B::perlstring( $_->role ), @roles
1015             ),
1016             );
1017             },
1018             }
1019             );
1020              
1021             $meta->add_type(
1022             {
1023             name => "HasMethods",
1024             parent => $_obj,
1025             constraint_generator => sub {
1026             return $meta->get_type( 'HasMethods' ) unless @_;
1027             require B;
1028             require Type::Tiny::Duck;
1029             return "Type::Tiny::Duck"->new(
1030             methods => \@_,
1031             display_name =>
1032             sprintf( 'HasMethods[%s]', join q[,], map B::perlstring( $_ ), @_ ),
1033             );
1034             },
1035             }
1036             );
1037              
1038             $meta->add_type(
1039             {
1040             name => "Enum",
1041             parent => $_str,
1042             constraint_generator => sub {
1043             return $meta->get_type( 'Enum' ) unless @_;
1044             my $coercion;
1045             if ( ref( $_[0] ) and ref( $_[0] ) eq 'SCALAR' ) {
1046             $coercion = ${ +shift };
1047             }
1048             elsif ( ref( $_[0] ) && !blessed( $_[0] )
1049             or blessed( $_[0] ) && $_[0]->isa( 'Type::Coercion' ) )
1050             {
1051             $coercion = shift;
1052             }
1053             require B;
1054             require Type::Tiny::Enum;
1055             return "Type::Tiny::Enum"->new(
1056             values => \@_,
1057             display_name => sprintf( 'Enum[%s]', join q[,], map B::perlstring( $_ ), @_ ),
1058             $coercion ? ( coercion => $coercion ) : (),
1059             );
1060             },
1061             type_default => undef,
1062             }
1063             );
1064              
1065             $meta->add_coercion(
1066             {
1067             name => "MkOpt",
1068             type_constraint => $meta->get_type( "OptList" ),
1069             type_coercion_map => [
1070             $_arr, q{ Exporter::Tiny::mkopt($_) },
1071             $_hash, q{ Exporter::Tiny::mkopt($_) },
1072             $_undef, q{ [] },
1073             ],
1074             }
1075             );
1076              
1077             $meta->add_coercion(
1078             {
1079             name => "Join",
1080             type_constraint => $_str,
1081             coercion_generator => sub {
1082             my ( $self, $target, $sep ) = @_;
1083             Types::TypeTiny::is_StringLike( $sep )
1084             or _croak( "Parameter to Join[`a] expected to be a string; got $sep" );
1085             require B;
1086             $sep = B::perlstring( $sep );
1087             return ( ArrayRef(), qq{ join($sep, \@\$_) } );
1088             },
1089             }
1090             );
1091              
1092             $meta->add_coercion(
1093             {
1094             name => "Split",
1095             type_constraint => $_arr,
1096             coercion_generator => sub {
1097             my ( $self, $target, $re ) = @_;
1098             ref( $re ) eq q(Regexp)
1099             or _croak(
1100             "Parameter to Split[`a] expected to be a regular expresssion; got $re" );
1101             my $regexp_string = "$re";
1102             $regexp_string =~ s/\\\//\\\\\//g; # toothpicks
1103             return ( Str(), qq{ [split /$regexp_string/, \$_] } );
1104             },
1105             }
1106             );
1107              
1108             __PACKAGE__->meta->make_immutable;
1109              
1110             1;
1111              
1112             __END__