File Coverage

blib/lib/Type/Tiny/XS.pm
Criterion Covered Total %
statement 96 109 88.0
branch 47 70 67.1
condition 11 21 52.3
subroutine 14 15 93.3
pod 3 3 100.0
total 171 218 78.4


line stmt bran cond sub pod time code
1 18     18   146880 use 5.008005;
  18         91  
2 18     18   97 use strict;
  18         32  
  18         405  
3 18     18   83 use warnings;
  18         51  
  18         464  
4 18     18   88 use XSLoader ();
  18         35  
  18         1412  
5              
6             package Type::Tiny::XS;
7              
8             our $AUTHORITY = 'cpan:TOBYINK';
9             our $VERSION = '0.024';
10              
11             __PACKAGE__->XSLoader::load( $VERSION );
12              
13 18     18   141 use Scalar::Util qw(refaddr);
  18         46  
  18         4254  
14              
15             my %names = (
16             map +( $_ => __PACKAGE__ . "::$_" ), qw/
17             Any ArrayRef Bool ClassName CodeRef Defined
18             FileHandle GlobRef HashRef Int Num Object
19             Ref RegexpRef ScalarRef Str Undef Value
20             PositiveInt PositiveOrZeroInt NonEmptyStr
21             ArrayLike HashLike CodeLike StringLike
22             Map Tuple Enum AnyOf AllOf
23             /
24             );
25             $names{Item} = $names{Any};
26              
27             if ( $] lt '5.010000' ) {
28             require MRO::Compat;
29             *Type::Tiny::XS::Util::get_linear_isa = \&mro::get_linear_isa;
30            
31             my $overloaded = sub {
32             require overload;
33             overload::Overloaded( ref $_[0] or $_[0] )
34             and overload::Method( ( ref $_[0] or $_[0] ), $_[1] );
35             };
36            
37 18     18   133 no warnings qw( uninitialized redefine once );
  18         46  
  18         6673  
38             *StringLike = sub {
39             defined( $_[0] ) && !ref( $_[0] )
40             or Scalar::Util::blessed( $_[0] ) && $overloaded->( $_[0], q[""] );
41             };
42             *CodeLike = sub {
43             ref( $_[0] ) eq 'CODE'
44             or Scalar::Util::blessed( $_[0] ) && $overloaded->( $_[0], q[&{}] );
45             };
46             *HashLike = sub {
47             ref( $_[0] ) eq 'HASH'
48             or Scalar::Util::blessed( $_[0] ) && $overloaded->( $_[0], q[%{}] );
49             };
50             *ArrayLike = sub {
51             ref( $_[0] ) eq 'ARRAY'
52             or Scalar::Util::blessed( $_[0] ) && $overloaded->( $_[0], q[@{}] );
53             };
54             } #/ if ( $] < '5.010000' [)
55              
56             my %coderefs;
57              
58             sub _know {
59 579     579   1159 my ( $coderef, $type ) = @_;
60 579         1728 $coderefs{ refaddr( $coderef ) } = $type;
61             }
62              
63             sub is_known {
64 3     3 1 1869 my $coderef = shift;
65 3         17 $coderefs{ refaddr( $coderef ) };
66             }
67              
68             for ( reverse sort keys %names ) {
69 18     18   137 no strict qw(refs);
  18         33  
  18         1658  
70             _know \&{ $names{$_} }, $_;
71             }
72              
73             my $id = 0;
74              
75             sub get_coderef_for {
76 44     44 1 33759 my $type = $_[0];
77            
78             return do {
79 18     18   121 no strict qw(refs);
  18         43  
  18         18618  
80 23         35 \&{ $names{$type} };
  23         121  
81 44 100       139 } if exists $names{$type};
82            
83 21         47 my $made;
84            
85 21 100 66     287 if ( $type =~ /^ArrayRef\[(.+)\]$/ ) {
    100 66        
    100          
    100          
    100          
    100          
    50          
    100          
    50          
    100          
    50          
86 7 50       60 my $child = get_coderef_for( $1 ) or return;
87 7         42 $made = _parameterize_ArrayRef_for( $child );
88             }
89            
90             elsif ( $] ge '5.010000' and $type =~ /^ArrayLike\[(.+)\]$/ ) {
91 1 50       4 my $child = get_coderef_for( $1 ) or return;
92 1         5 $made = _parameterize_ArrayLike_for( $child );
93             }
94            
95             elsif ( $type =~ /^HashRef\[(.+)\]$/ ) {
96 2 50       29 my $child = get_coderef_for( $1 ) or return;
97 2         14 $made = _parameterize_HashRef_for( $child );
98             }
99            
100             elsif ( $] ge '5.010000' and $type =~ /^HashLike\[(.+)\]$/ ) {
101 1 50       3 my $child = get_coderef_for( $1 ) or return;
102 1         6 $made = _parameterize_HashLike_for( $child );
103             }
104            
105             elsif ( $type =~ /^Map\[(.+),(.+)\]$/ ) {
106 2         4 my @children;
107 2 50       3 if ( eval { require Type::Parser } ) {
  2         551  
108 2         7622 @children = map scalar( get_coderef_for( $_ ) ), _parse_parameters( $type );
109             }
110             else {
111 0         0 push @children, get_coderef_for( $1 );
112 0         0 push @children, get_coderef_for( $2 );
113             }
114 2 50       7 @children == 2 or return;
115 2   50     10 defined or return for @children;
116 2         13 $made = _parameterize_Map_for( \@children );
117             } #/ elsif ( $type =~ /^Map\[(.+),(.+)\]$/)
118            
119             elsif ( $type =~ /^(AnyOf|AllOf|Tuple)\[(.+)\]$/ ) {
120 4         11 my $base = $1;
121             my @children =
122             map scalar( get_coderef_for( $_ ) ),
123 4 50       8 ( eval { require Type::Parser } )
  4         499  
124             ? _parse_parameters( $type )
125             : split( /,/, $2 );
126 4   50     22 defined or return for @children;
127 4         39 my $maker = __PACKAGE__->can( "_parameterize_${base}_for" );
128 4 50       24 $made = $maker->( \@children ) if $maker;
129             } #/ elsif ( $type =~ /^(AnyOf|AllOf|Tuple)\[(.+)\]$/)
130            
131             elsif ( $type =~ /^Maybe\[(.+)\]$/ ) {
132 0 0       0 my $child = get_coderef_for( $1 ) or return;
133 0         0 $made = _parameterize_Maybe_for( $child );
134             }
135            
136             elsif ( $type =~ /^InstanceOf\[(.+)\]$/ ) {
137 1         3 my $class = $1;
138 1 50       7 return unless Type::Tiny::XS::Util::is_valid_class_name( $class );
139 1         13 $made = Type::Tiny::XS::Util::generate_isa_predicate_for( $class );
140             }
141            
142             elsif ( $type =~ /^HasMethods\[(.+)\]$/ ) {
143 0         0 my $methods = [ sort( split /,/, $1 ) ];
144 0   0     0 /^[^\W0-9]\w*$/ or return for @$methods;
145 0         0 $made = Type::Tiny::XS::Util::generate_can_predicate_for( $methods );
146             }
147            
148             # Type::Tiny::Enum > 1.010003 double-quotes its enums
149             elsif ( $type =~ /^Enum\[".*"\]$/ ) {
150 1 50       3 if ( eval { require Type::Parser } ) {
  1         10  
151 1         3 my $parsed = Type::Parser::parse( $type );
152 1 50       19297 if ( $parsed->{type} eq "parameterized" ) {
153 1         3 my @todo = $parsed->{params};
154 1         3 my @strings;
155             my $bad;
156 1         4 while ( my $todo = shift @todo ) {
157 6 100 66     42 if ( $todo->{type} eq 'list' ) {
    100 33        
    50          
158 1         3 push @todo, @{ $todo->{list} };
  1         4  
159             }
160             elsif ( $todo->{type} eq "expression"
161             && $todo->{op}->type eq Type::Parser::COMMA() )
162             {
163 2         23 push @todo, $todo->{lhs}, $todo->{rhs};
164             }
165             elsif ( $todo->{type} eq "primary" && $todo->{token}->type eq "QUOTELIKE" ) {
166 3         17 push @strings, eval( $todo->{token}->spelling );
167             }
168             else {
169             # Unexpected entry in the parse-tree, bail out
170 0         0 $bad = 1;
171             }
172             } #/ while ( my $todo = shift ...)
173 1 50       20 $made = _parameterize_Enum_for( \@strings ) unless $bad;
174             } #/ if ( $parsed->{type} eq...)
175             } #/ if ( eval { require Type::Parser...})
176             } #/ elsif ( $type =~ /^Enum\[".*"\]$/)
177            
178             elsif ( $type =~ /^Enum\[(.+)\]$/ ) {
179 2         14 my $strings = [ sort( split /,/, $1 ) ];
180 2         13 $made = _parameterize_Enum_for( $strings );
181             }
182            
183 21 50       61 if ( $made ) {
184 18     18   161 no strict qw(refs);
  18         45  
  18         9005  
185 21         122 my $slot = sprintf( '%s::AUTO::TC%d', __PACKAGE__, ++$id );
186 21         87 $names{$type} = $slot;
187 21         59 _know( $made, $type );
188 21         162 *$slot = $made;
189 21         83 return $made;
190             }
191            
192 0         0 return;
193             } #/ sub get_coderef_for
194              
195             sub get_subname_for {
196 0     0 1 0 my $type = $_[0];
197 0 0       0 get_coderef_for( $type ) unless exists $names{$type};
198 0         0 $names{$type};
199             }
200              
201             sub _parse_parameters {
202 6     6   7354 my $got = Type::Parser::parse( @_ );
203 6 50       8325 $got->{params} or return;
204 6         17 _handle_expr( $got->{params} );
205             }
206              
207             sub _handle_expr {
208 51     51   93 my $e = shift;
209            
210 51 100       102 if ( $e->{type} eq 'list' ) {
211 11         20 return map _handle_expr( $_ ), @{ $e->{list} };
  11         74  
212             }
213 40 100       78 if ( $e->{type} eq 'parameterized' ) {
214 5         13 my ( $base ) = _handle_expr( $e->{base} );
215 5         34 my @params = _handle_expr( $e->{params} );
216 5         86 return sprintf( '%s[%s]', $base, join( q[,], @params ) );
217             }
218 35 100 66     90 if ( $e->{type} eq 'expression' and $e->{op}->type eq Type::Parser::COMMA() ) {
219 12         76 return _handle_expr( $e->{lhs} ), _handle_expr( $e->{rhs} );
220             }
221 23 50       48 if ( $e->{type} eq 'primary' ) {
222 23         62 return $e->{token}->spelling;
223             }
224            
225 0           '****';
226             } #/ sub _handle_expr
227              
228             1;
229              
230             __END__