File Coverage

blib/lib/Type/Tiny/Intersection.pm
Criterion Covered Total %
statement 99 101 99.0
branch 36 44 81.8
condition 8 10 80.0
subroutine 26 26 100.0
pod 12 12 100.0
total 181 193 94.3


line stmt bran cond sub pod time code
1             package Type::Tiny::Intersection;
2              
3 19     19   1973 use 5.008001;
  19         73  
4 19     19   113 use strict;
  19         35  
  19         418  
5 19     19   93 use warnings;
  19         40  
  19         931  
6              
7             BEGIN {
8 19     19   65 $Type::Tiny::Intersection::AUTHORITY = 'cpan:TOBYINK';
9 19         843 $Type::Tiny::Intersection::VERSION = '2.004000';
10             }
11              
12             $Type::Tiny::Intersection::VERSION =~ tr/_//d;
13              
14 19     19   125 use Scalar::Util qw< blessed >;
  19         40  
  19         1152  
15 19     19   154 use Types::TypeTiny ();
  19         46  
  19         1149  
16              
17 4     4   23 sub _croak ($;@) { require Error::TypeTiny; goto \&Error::TypeTiny::croak }
  4         17  
18              
19 19     19   120 use Type::Tiny ();
  19         40  
  19         30769  
20             our @ISA = 'Type::Tiny';
21              
22             __PACKAGE__->_install_overloads(
23 342   50 342   1687 q[@{}] => sub { $_[0]{type_constraints} ||= [] },
24             );
25              
26             sub new_by_overload {
27 40067     40067 1 73265 my $proto = shift;
28 40067 50       127959 my %opts = ( @_ == 1 ) ? %{ $_[0] } : @_;
  0         0  
29              
30 40067         66706 my @types = @{ $opts{type_constraints} };
  40067         95138  
31 40067 50 100     223742 if ( my @makers = map scalar( blessed($_) && $_->can( 'new_intersection' ) ), @types ) {
32 40067         73259 my $first_maker = shift @makers;
33 40067 100       98716 if ( ref $first_maker ) {
34 1         5 my $all_same = not grep $_ ne $first_maker, @makers;
35 1 50       5 if ( $all_same ) {
36 1         6 return ref( $types[0] )->$first_maker( %opts );
37             }
38             }
39             }
40              
41 40066         111486 return $proto->new( \%opts );
42             }
43              
44             sub new {
45 40090     40090 1 75294 my $proto = shift;
46            
47 40090 100       82980 my %opts = ( @_ == 1 ) ? %{ $_[0] } : @_;
  40066         134381  
48             _croak "Intersection type constraints cannot have a parent constraint"
49 40090 100       97458 if exists $opts{parent};
50             _croak
51             "Intersection type constraints cannot have a constraint coderef passed to the constructor"
52 40089 100       77681 if exists $opts{constraint};
53             _croak
54             "Intersection type constraints cannot have a inlining coderef passed to the constructor"
55 40088 100       80835 if exists $opts{inlined};
56             _croak "Need to supply list of type constraints"
57 40087 100       82543 unless exists $opts{type_constraints};
58            
59             $opts{type_constraints} = [
60 80183 100       195452 map { $_->isa( __PACKAGE__ ) ? @$_ : $_ }
61             map Types::TypeTiny::to_TypeTiny( $_ ),
62             @{
63 40086         62888 ref $opts{type_constraints} eq "ARRAY"
64             ? $opts{type_constraints}
65 40086 50       190536 : [ $opts{type_constraints} ]
66             }
67             ];
68            
69 40086         77806 if ( Type::Tiny::_USE_XS ) {
70 40086         61830 my @constraints = @{ $opts{type_constraints} };
  40086         94468  
71             my @known = map {
72 40086         68989 my $known = Type::Tiny::XS::is_known( $_->compiled_check );
  80207         178221  
73 80207 100       511223 defined( $known ) ? $known : ();
74             } @constraints;
75            
76 40086 100       108367 if ( @known == @constraints ) {
77 17         114 my $xsub = Type::Tiny::XS::get_coderef_for(
78             sprintf "AllOf[%s]",
79             join( ',', @known )
80             );
81 17 50       685 $opts{compiled_type_constraint} = $xsub if $xsub;
82             }
83             } #/ if ( Type::Tiny::_USE_XS)
84            
85 40086         137898 return $proto->SUPER::new( %opts );
86             } #/ sub new
87              
88             sub _lockdown {
89 40086     40086   96569 my ( $self, $callback ) = @_;
90 40086         110152 $callback->( $self->{type_constraints} );
91             }
92              
93 44     44 1 151 sub type_constraints { $_[0]{type_constraints} }
94 55   66 55 1 239 sub constraint { $_[0]{constraint} ||= $_[0]->_build_constraint }
95              
96 110     110   311 sub _is_null_constraint { 0 }
97              
98             sub _build_display_name {
99 41     41   80 my $self = shift;
100 41         107 join q[&], @$self;
101             }
102              
103             sub _build_constraint {
104 26     26   49 my @checks = map $_->compiled_check, @{ +shift };
  26         66  
105             return sub {
106 68     68   140 my $val = $_;
107 68   100     247 $_->( $val ) || return for @checks;
108 47         249 return !!1;
109             }
110 26         171 }
111              
112             sub can_be_inlined {
113 119     119 1 871 my $self = shift;
114 119         313 not grep !$_->can_be_inlined, @$self;
115             }
116              
117             sub inline_check {
118 114     114 1 246 my $self = shift;
119            
120 114 100       304 if ( Type::Tiny::_USE_XS and !exists $self->{xs_sub} ) {
121 34         65 $self->{xs_sub} = undef;
122            
123 34         53 my @constraints = @{ $self->type_constraints };
  34         70  
124             my @known = map {
125 34         66 my $known = Type::Tiny::XS::is_known( $_->compiled_check );
  71         162  
126 71 100       533 defined( $known ) ? $known : ();
127             } @constraints;
128            
129 34 100       110 if ( @known == @constraints ) {
130 7         31 $self->{xs_sub} = Type::Tiny::XS::get_subname_for(
131             sprintf "AllOf[%s]",
132             join( ',', @known )
133             );
134             }
135             } #/ if ( Type::Tiny::_USE_XS...)
136            
137 114         347 my $code = sprintf '(%s)', join " and ", map $_->inline_check( $_[0] ), @$self;
138            
139 114 100       1710 return "do { $Type::Tiny::SafePackage $code }"
140             if $Type::Tiny::AvoidCallbacks;
141             return "$self->{xs_sub}\($_[0]\)"
142 94 100       290 if $self->{xs_sub};
143 80         1756 return $code;
144             } #/ sub inline_check
145              
146             sub has_parent {
147 71     71 1 117 !!@{ $_[0]{type_constraints} };
  71         308  
148             }
149              
150             sub parent {
151 111     111 1 308 $_[0]{type_constraints}[0];
152             }
153              
154             sub validate_explain {
155 1     1 1 3 my $self = shift;
156 1         3 my ( $value, $varname ) = @_;
157 1 50       3 $varname = '$_' unless defined $varname;
158            
159 1 50       8 return undef if $self->check( $value );
160            
161 1         489 require Type::Utils;
162 1         6 for my $type ( @$self ) {
163 1         6 my $deep = $type->validate_explain( $value, $varname );
164             return [
165 1 50       7 sprintf(
166             '"%s" requires that the value pass %s',
167             $self,
168             Type::Utils::english_list( map qq["$_"], @$self ),
169             ),
170             @$deep,
171             ] if $deep;
172             } #/ for my $type ( @$self )
173            
174             # This should never happen...
175 0         0 return; # uncoverable statement
176             } #/ sub validate_explain
177              
178             my $_delegate = sub {
179             my ( $self, $method ) = ( shift, shift );
180             my @types = @{ $self->type_constraints };
181             my $found = 0;
182             for my $i ( 0 .. $#types ) {
183             my $type = $types[$i];
184             if ( $type->can( $method ) ) {
185             $types[$i] = $type->$method( @_ );
186             ++$found;
187             last;
188             }
189             }
190             _croak(
191             'Could not apply method %s to any type within the intersection',
192             $method
193             ) unless $found;
194             ref( $self )->new( type_constraints => \@types );
195             };
196              
197             sub stringifies_to {
198 1     1 1 9 my $self = shift;
199 1         5 $self->$_delegate( stringifies_to => @_ );
200             }
201              
202             sub numifies_to {
203 2     2 1 13 my $self = shift;
204 2         6 $self->$_delegate( numifies_to => @_ );
205             }
206              
207             sub with_attribute_values {
208 2     2 1 4 my $self = shift;
209 2         6 $self->$_delegate( with_attribute_values => @_ );
210             }
211              
212             my $comparator;
213             $comparator = sub {
214             my $A = shift->find_constraining_type;
215             my $B = shift->find_constraining_type;
216            
217             if ( $A->isa( __PACKAGE__ ) ) {
218             my @A_constraints = map $_->find_constraining_type, @{ $A->type_constraints };
219            
220             my @A_equal_to_B = grep $_->equals( $B ), @A_constraints;
221             if ( @A_equal_to_B == @A_constraints ) {
222             return Type::Tiny::CMP_EQUIVALENT();
223             }
224            
225             my @A_subs_of_B = grep $_->is_a_type_of( $B ), @A_constraints;
226             if ( @A_subs_of_B ) {
227             return Type::Tiny::CMP_SUBTYPE();
228             }
229             } #/ if ( $A->isa( __PACKAGE__...))
230            
231             elsif ( $B->isa( __PACKAGE__ ) ) {
232             my $r = $comparator->( $B, $A );
233             return $r if $r eq Type::Tiny::CMP_EQUIVALENT();
234             return -$r if $r eq Type::Tiny::CMP_SUBTYPE();
235             }
236            
237             return Type::Tiny::CMP_UNKNOWN();
238             };
239             push @Type::Tiny::CMP, $comparator;
240              
241             1;
242              
243             __END__