Union inlining should always be surrounded by its own set of parens, for safety
[gitmo/Moose.git] / lib / Moose / Meta / TypeConstraint / Union.pm
CommitLineData
8ee73eeb 1
2package Moose::Meta::TypeConstraint::Union;
3
4use strict;
5use warnings;
6use metaclass;
7
3726f905 8use Moose::Meta::TypeCoercion::Union;
9
09532816 10use List::MoreUtils qw(all);
1aae641c 11use List::Util qw(first);
12
d67145ed 13use base 'Moose::Meta::TypeConstraint';
14
8ee73eeb 15__PACKAGE__->meta->add_attribute('type_constraints' => (
16 accessor => 'type_constraints',
17 default => sub { [] }
18));
19
d03bd989 20sub new {
3726f905 21 my ($class, %options) = @_;
816ef2e2 22
23 my $name = join '|' => sort { $a cmp $b }
24 map { $_->name } @{ $options{type_constraints} };
25
3726f905 26 my $self = $class->SUPER::new(
816ef2e2 27 name => $name,
28 %options,
3726f905 29 );
816ef2e2 30
3726f905 31 $self->_set_constraint(sub { $self->check($_[0]) });
4beacd39 32
70f676ca 33 return $self;
34}
35
36# XXX - this is a rather gross implementation of laziness for the benefit of
37# MX::Types. If we try to call ->has_coercion on the objects during object
38# construction, this does not work when defining a recursive constraint with
39# MX::Types.
40sub coercion {
41 my $self = shift;
42
43 return $self->{coercion} if exists $self->{coercion};
44
5c56b608 45 # Using any instead of grep here causes a weird error with some corner
46 # cases when MX::Types is in use. See RT #61001.
47 if ( grep { $_->has_coercion } @{ $self->type_constraints } ) {
70f676ca 48 return $self->{coercion} = Moose::Meta::TypeCoercion::Union->new(
49 type_constraint => $self );
50 }
51 else {
52 return $self->{coercion} = undef;
4beacd39 53 }
70f676ca 54}
4beacd39 55
70f676ca 56sub has_coercion {
57 return defined $_[0]->coercion;
8ee73eeb 58}
59
816ef2e2 60sub _actually_compile_type_constraint {
61 my $self = shift;
62
63 my @constraints = @{ $self->type_constraints };
64
65 return sub {
66 my $value = shift;
67 foreach my $type (@constraints) {
68 return 1 if $type->check($value);
69 }
70 return undef;
71 };
72}
73
7c047a36 74sub can_be_inlined {
09532816 75 my $self = shift;
76
7c047a36 77 return all { $_->can_be_inlined } @{ $self->type_constraints };
09532816 78}
79
80sub _inline_check {
81 my $self = shift;
82 my $val = shift;
83
576c9cfc 84 return '('
85 . (
86 join ' || ', map { '(' . $_->_inline_check($val) . ')' }
87 @{ $self->type_constraints }
88 )
89 . ')';
09532816 90};
816ef2e2 91
ca789903 92sub inline_environment {
93 my $self = shift;
94
95 return { map { %{ $_->inline_environment } }
96 @{ $self->type_constraints } };
97}
98
dabed765 99sub equals {
100 my ( $self, $type_or_name ) = @_;
101
102 my $other = Moose::Util::TypeConstraints::find_type_constraint($type_or_name);
103
104 return unless $other->isa(__PACKAGE__);
105
106 my @self_constraints = @{ $self->type_constraints };
107 my @other_constraints = @{ $other->type_constraints };
108
109 return unless @self_constraints == @other_constraints;
110
111 # FIXME presort type constraints for efficiency?
112 constraint: foreach my $constraint ( @self_constraints ) {
113 for ( my $i = 0; $i < @other_constraints; $i++ ) {
114 if ( $constraint->equals($other_constraints[$i]) ) {
115 splice @other_constraints, $i, 1;
116 next constraint;
117 }
118 }
119 }
120
121 return @other_constraints == 0;
122}
123
124sub parents {
125 my $self = shift;
126 $self->type_constraints;
127}
128
8ee73eeb 129sub validate {
3726f905 130 my ($self, $value) = @_;
8ee73eeb 131 my $message;
132 foreach my $type (@{$self->type_constraints}) {
133 my $err = $type->validate($value);
134 return unless defined $err;
135 $message .= ($message ? ' and ' : '') . $err
136 if defined $err;
137 }
d03bd989 138 return ($message . ' in (' . $self->name . ')') ;
8ee73eeb 139}
140
1aae641c 141sub find_type_for {
142 my ($self, $value) = @_;
143
144 return first { $_->check($value) } @{ $self->type_constraints };
145}
146
8ee73eeb 147sub is_a_type_of {
148 my ($self, $type_name) = @_;
149 foreach my $type (@{$self->type_constraints}) {
150 return 1 if $type->is_a_type_of($type_name);
151 }
d03bd989 152 return 0;
8ee73eeb 153}
154
155sub is_subtype_of {
156 my ($self, $type_name) = @_;
157 foreach my $type (@{$self->type_constraints}) {
158 return 1 if $type->is_subtype_of($type_name);
159 }
160 return 0;
161}
162
85a9908f 163sub create_child_type {
622c9332 164 my ( $self, %opts ) = @_;
165
166 my $constraint
167 = Moose::Meta::TypeConstraint->new( %opts, parent => $self );
168
9ceb576e 169 # if we have a type constraint union, and no
170 # type check, this means we are just aliasing
171 # the union constraint, which means we need to
172 # handle this differently.
173 # - SL
622c9332 174 if ( not( defined $opts{constraint} )
175 && $self->has_coercion ) {
176 $constraint->coercion(
177 Moose::Meta::TypeCoercion::Union->new(
178 type_constraint => $self,
179 )
180 );
9ceb576e 181 }
622c9332 182
9ceb576e 183 return $constraint;
184}
185
8ee73eeb 1861;
187
ad46f524 188# ABSTRACT: A union of Moose type constraints
189
8ee73eeb 190__END__
191
192=pod
193
39b3bc94 194=head1 DESCRIPTION
195
ae2f99ea 196This metaclass represents a union of type constraints. A union takes
197multiple type constraints, and is true if any one of its member
198constraints is true.
ecb59493 199
ae2f99ea 200=head1 INHERITANCE
ecb59493 201
ae2f99ea 202C<Moose::Meta::TypeConstraint::Union> is a subclass of
203L<Moose::Meta::TypeConstraint>.
ecb59493 204
39b3bc94 205=over 4
206
ae2f99ea 207=item B<< Moose::Meta::TypeConstraint::Union->new(%options) >>
39b3bc94 208
ae2f99ea 209This creates a new class type constraint based on the given
210C<%options>.
1b58cb9f 211
ae2f99ea 212It takes the same options as its parent. It also requires an
213additional option, C<type_constraints>. This is an array reference
214containing the L<Moose::Meta::TypeConstraint> objects that are the
215members of the union type. The C<name> option defaults to the names
216all of these member types sorted and then joined by a pipe (|).
dabed765 217
ae2f99ea 218The constructor sets the implementation of the constraint so that is
219simply calls C<check> on the newly created object.
ecb59493 220
ae2f99ea 221Finally, the constructor also makes sure that the object's C<coercion>
222attribute is a L<Moose::Meta::TypeCoercion::Union> object.
ecb59493 223
ae2f99ea 224=item B<< $constraint->type_constraints >>
ecb59493 225
ae2f99ea 226This returns the array reference of C<type_constraints> provided to
227the constructor.
39b3bc94 228
ae2f99ea 229=item B<< $constraint->parents >>
39b3bc94 230
ae2f99ea 231This returns the same constraint as the C<type_constraints> method.
39b3bc94 232
ae2f99ea 233=item B<< $constraint->check($value) >>
39b3bc94 234
ae2f99ea 235=item B<< $constraint->validate($value) >>
39b3bc94 236
ae2f99ea 237These two methods simply call the relevant method on each of the
238member type constraints in the union. If any type accepts the value,
239the value is valid.
39b3bc94 240
ae2f99ea 241With C<validate> the error message returned includes all of the error
242messages returned by the member type constraints.
39b3bc94 243
ae2f99ea 244=item B<< $constraint->equals($type_name_or_object) >>
39b3bc94 245
ae2f99ea 246A type is considered equal if it is also a union type, and the two
247unions have the same member types.
39b3bc94 248
1aae641c 249=item B<< $constraint->find_type_for($value) >>
250
251This returns the first member type constraint for which C<check($value)> is
252true, allowing you to determine which of the Union's member type constraints
253a given value matches.
254
ae2f99ea 255=item B<< $constraint->is_a_type_of($type_name_or_object) >>
ecb59493 256
ae2f99ea 257This returns true if any of the member type constraints return true
258for the C<is_a_type_of> method.
ecb59493 259
ae2f99ea 260=item B<< $constraint->is_subtype_of >>
39b3bc94 261
ae2f99ea 262This returns true if any of the member type constraints return true
263for the C<is_a_subtype_of> method.
0eec94be 264
ae2f99ea 265=item B<< $constraint->create_child_type(%options) >>
0eec94be 266
ae2f99ea 267This returns a new L<Moose::Meta::TypeConstraint> object with the type
268as its parent.
9ceb576e 269
39b3bc94 270=back
271
272=head1 BUGS
273
d4048ef3 274See L<Moose/BUGS> for details on reporting bugs.
39b3bc94 275
8ee73eeb 276=cut