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   2826 use 5.008001;
  19         80  
4 19     19   109 use strict;
  19         47  
  19         605  
5 19     19   105 use warnings;
  19         71  
  19         871  
6              
7             BEGIN {
8 19     19   56 $Type::Tiny::Intersection::AUTHORITY = 'cpan:TOBYINK';
9 19         819 $Type::Tiny::Intersection::VERSION = '2.002001';
10             }
11              
12             $Type::Tiny::Intersection::VERSION =~ tr/_//d;
13              
14 19     19   138 use Scalar::Util qw< blessed >;
  19         375  
  19         1114  
15 19     19   121 use Types::TypeTiny ();
  19         53  
  19         1152  
16              
17 4     4   422 sub _croak ($;@) { require Error::TypeTiny; goto \&Error::TypeTiny::croak }
  4         18  
18              
19 19     19   118 use Type::Tiny ();
  19         47  
  19         32648  
20             our @ISA = 'Type::Tiny';
21              
22             __PACKAGE__->_install_overloads(
23 342   50 342   1812 q[@{}] => sub { $_[0]{type_constraints} ||= [] },
24             );
25              
26             sub new_by_overload {
27 40067     40067 1 69689 my $proto = shift;
28 40067 50       124813 my %opts = ( @_ == 1 ) ? %{ $_[0] } : @_;
  0         0  
29              
30 40067         62665 my @types = @{ $opts{type_constraints} };
  40067         85250  
31 40067 50 100     206245 if ( my @makers = map scalar( blessed($_) && $_->can( 'new_intersection' ) ), @types ) {
32 40067         76404 my $first_maker = shift @makers;
33 40067 100       91335 if ( ref $first_maker ) {
34 1         4 my $all_same = not grep $_ ne $first_maker, @makers;
35 1 50       3 if ( $all_same ) {
36 1         9 return ref( $types[0] )->$first_maker( %opts );
37             }
38             }
39             }
40              
41 40066         105172 return $proto->new( \%opts );
42             }
43              
44             sub new {
45 40090     40090 1 63986 my $proto = shift;
46            
47 40090 100       88704 my %opts = ( @_ == 1 ) ? %{ $_[0] } : @_;
  40066         137024  
48             _croak "Intersection type constraints cannot have a parent constraint"
49 40090 100       90363 if exists $opts{parent};
50             _croak
51             "Intersection type constraints cannot have a constraint coderef passed to the constructor"
52 40089 100       82999 if exists $opts{constraint};
53             _croak
54             "Intersection type constraints cannot have a inlining coderef passed to the constructor"
55 40088 100       80729 if exists $opts{inlined};
56             _croak "Need to supply list of type constraints"
57 40087 100       89531 unless exists $opts{type_constraints};
58            
59             $opts{type_constraints} = [
60 80183 100       182420 map { $_->isa( __PACKAGE__ ) ? @$_ : $_ }
61             map Types::TypeTiny::to_TypeTiny( $_ ),
62             @{
63 40086         60914 ref $opts{type_constraints} eq "ARRAY"
64             ? $opts{type_constraints}
65 40086 50       167326 : [ $opts{type_constraints} ]
66             }
67             ];
68            
69 40086         78658 if ( Type::Tiny::_USE_XS ) {
70 40086         59141 my @constraints = @{ $opts{type_constraints} };
  40086         95490  
71             my @known = map {
72 40086         73156 my $known = Type::Tiny::XS::is_known( $_->compiled_check );
  80207         167288  
73 80207 100       523071 defined( $known ) ? $known : ();
74             } @constraints;
75            
76 40086 100       109099 if ( @known == @constraints ) {
77 17         119 my $xsub = Type::Tiny::XS::get_coderef_for(
78             sprintf "AllOf[%s]",
79             join( ',', @known )
80             );
81 17 50       758 $opts{compiled_type_constraint} = $xsub if $xsub;
82             }
83             } #/ if ( Type::Tiny::_USE_XS)
84            
85 40086         136400 return $proto->SUPER::new( %opts );
86             } #/ sub new
87              
88             sub _lockdown {
89 40086     40086   80713 my ( $self, $callback ) = @_;
90 40086         116097 $callback->( $self->{type_constraints} );
91             }
92              
93 44     44 1 135 sub type_constraints { $_[0]{type_constraints} }
94 55   66 55 1 229 sub constraint { $_[0]{constraint} ||= $_[0]->_build_constraint }
95              
96 110     110   316 sub _is_null_constraint { 0 }
97              
98             sub _build_display_name {
99 41     41   85 my $self = shift;
100 41         135 join q[&], @$self;
101             }
102              
103             sub _build_constraint {
104 26     26   53 my @checks = map $_->compiled_check, @{ +shift };
  26         61  
105             return sub {
106 68     68   103 my $val = $_;
107 68   100     248 $_->( $val ) || return for @checks;
108 47         249 return !!1;
109             }
110 26         173 }
111              
112             sub can_be_inlined {
113 119     119 1 818 my $self = shift;
114 119         293 not grep !$_->can_be_inlined, @$self;
115             }
116              
117             sub inline_check {
118 114     114 1 245 my $self = shift;
119            
120 114 100       270 if ( Type::Tiny::_USE_XS and !exists $self->{xs_sub} ) {
121 34         84 $self->{xs_sub} = undef;
122            
123 34         46 my @constraints = @{ $self->type_constraints };
  34         82  
124             my @known = map {
125 34         70 my $known = Type::Tiny::XS::is_known( $_->compiled_check );
  71         169  
126 71 100       465 defined( $known ) ? $known : ();
127             } @constraints;
128            
129 34 100       101 if ( @known == @constraints ) {
130 7         37 $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         323 my $code = sprintf '(%s)', join " and ", map $_->inline_check( $_[0] ), @$self;
138            
139 114 100       1775 return "do { $Type::Tiny::SafePackage $code }"
140             if $Type::Tiny::AvoidCallbacks;
141             return "$self->{xs_sub}\($_[0]\)"
142 94 100       329 if $self->{xs_sub};
143 80         1875 return $code;
144             } #/ sub inline_check
145              
146             sub has_parent {
147 70     70 1 105 !!@{ $_[0]{type_constraints} };
  70         326  
148             }
149              
150             sub parent {
151 110     110 1 300 $_[0]{type_constraints}[0];
152             }
153              
154             sub validate_explain {
155 1     1 1 4 my $self = shift;
156 1         3 my ( $value, $varname ) = @_;
157 1 50       4 $varname = '$_' unless defined $varname;
158            
159 1 50       7 return undef if $self->check( $value );
160            
161 1         527 require Type::Utils;
162 1         5 for my $type ( @$self ) {
163 1         8 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 10 my $self = shift;
199 1         3 $self->$_delegate( stringifies_to => @_ );
200             }
201              
202             sub numifies_to {
203 2     2 1 18 my $self = shift;
204 2         6 $self->$_delegate( numifies_to => @_ );
205             }
206              
207             sub with_attribute_values {
208 2     2 1 5 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__