Simplify compile_type_constraint
[gitmo/Mouse.git] / lib / Mouse / Meta / TypeConstraint.pm
CommitLineData
684db121 1package Mouse::Meta::TypeConstraint;
bc69ee88 2use Mouse::Util qw(:meta); # enables strict and warnings
9c85e9dc 3
f5ee065f 4use overload
c489a477 5 'bool' => sub { 1 }, # always true
6
93540011 7 '""' => sub { $_[0]->name }, # stringify to tc name
8adc0b23 8
9 '|' => sub { # or-combination
10 require Mouse::Util::TypeConstraints;
11 return Mouse::Util::TypeConstraints::find_or_parse_type_constraint(
12 "$_[0] | $_[1]",
13 );
14 },
15
f5ee065f 16 fallback => 1;
684db121 17
ca352580 18use Carp ();
6d28c5cf 19
f5ee065f 20my $null_check = sub { 1 };
21
684db121 22sub new {
f5ee065f 23 my($class, %args) = @_;
24
25 $args{name} = '__ANON__' if !defined $args{name};
684db121 26
3b89ea91 27 my $check = delete $args{optimized};
28
29 if($args{_compiled_type_constraint}){
0126c27c 30 Carp::cluck("'_compiled_type_constraint' has been deprecated, use 'optimized' instead")
31 if _MOUSE_VERBOSE;
3b89ea91 32
0126c27c 33 $check = $args{_compiled_type_constraint};
3b89ea91 34 }
35
36 if($check){
37 $args{hand_optimized_type_constraint} = $check;
38 $args{compiled_type_constraint} = $check;
39 }
40
41 $check = $args{constraint};
f5ee065f 42
f5ee065f 43 if(defined($check) && ref($check) ne 'CODE'){
ca352580 44 Carp::confess("Constraint for $args{name} is not a CODE reference");
f5ee065f 45 }
46
3b89ea91 47 $args{package_defined_in} ||= caller;
48
f5ee065f 49 my $self = bless \%args, $class;
3b89ea91 50 $self->compile_type_constraint() if !$self->{hand_optimized_type_constraint};
f5ee065f 51
ffbbf459 52 if($self->{type_constraints}){ # Union
53 my @coercions;
54 foreach my $type(@{$self->{type_constraints}}){
55 if($type->has_coercion){
56 push @coercions, $type;
57 }
58 }
59 if(@coercions){
60 $self->{_compiled_type_coercion} = sub {
61 my($thing) = @_;
62 foreach my $type(@coercions){
63 my $value = $type->coerce($thing);
64 return $value if $self->check($value);
65 }
66 return $thing;
67 };
68 }
69 }
70
f5ee065f 71 return $self;
72}
73
74sub create_child_type{
75 my $self = shift;
e98220ab 76 # XXX: FIXME
77 return ref($self)->new(
3b89ea91 78 # a child inherits its parent's attributes
79 %{$self},
80
81 # but does not inherit 'compiled_type_constraint' and 'hand_optimized_type_constraint'
82 compiled_type_constraint => undef,
83 hand_optimized_type_constraint => undef,
84
85 # and is given child-specific args, of course.
86 @_,
87
88 # and its parent
89 parent => $self,
e98220ab 90 );
684db121 91}
92
f5ee065f 93
3b89ea91 94sub compile_type_constraint{
f5ee065f 95 my($self) = @_;
96
97 # add parents first
98 my @checks;
d2af4ecb 99 for(my $parent = $self->{parent}; defined $parent; $parent = $parent->{parent}){
3b89ea91 100 if($parent->{hand_optimized_type_constraint}){
c4b28dd2 101 unshift @checks, $parent->{hand_optimized_type_constraint};
3b89ea91 102 last; # a hand optimized constraint must include all the parents
103 }
104 elsif($parent->{constraint}){
c4b28dd2 105 unshift @checks, $parent->{constraint};
f5ee065f 106 }
107 }
3b89ea91 108
f5ee065f 109 # then add child
110 if($self->{constraint}){
111 push @checks, $self->{constraint};
112 }
113
3b89ea91 114 if($self->{type_constraints}){ # Union
d2af4ecb 115 my @types = map{ $_->{compiled_type_constraint} } @{ $self->{type_constraints} };
3b89ea91 116 push @checks, sub{
117 foreach my $c(@types){
118 return 1 if $c->($_[0]);
119 }
120 return 0;
121 };
122 }
123
f5ee065f 124 if(@checks == 0){
3b89ea91 125 $self->{compiled_type_constraint} = $null_check;
f5ee065f 126 }
f5ee065f 127 else{
3b89ea91 128 $self->{compiled_type_constraint} = sub{
f5ee065f 129 my(@args) = @_;
130 local $_ = $args[0];
131 foreach my $c(@checks){
132 return undef if !$c->(@args);
133 }
134 return 1;
135 };
136 }
3b89ea91 137 return;
90fe520e 138}
139
ffbbf459 140sub _add_type_coercions{
141 my $self = shift;
142
143 my $coercions = ($self->{_coercion_map} ||= []);
144 my %has = map{ $_->[0] => undef } @{$coercions};
145
146 for(my $i = 0; $i < @_; $i++){
147 my $from = $_[ $i];
148 my $action = $_[++$i];
149
150 if(exists $has{$from}){
ca352580 151 Carp::confess("A coercion action already exists for '$from'");
ffbbf459 152 }
153
154 my $type = Mouse::Util::TypeConstraints::find_or_parse_type_constraint($from)
ca352580 155 or Carp::confess("Could not find the type constraint ($from) to coerce from");
ffbbf459 156
157 push @{$coercions}, [ $type => $action ];
158 }
159
160 # compile
161 if(exists $self->{type_constraints}){ # union type
ca352580 162 Carp::confess("Cannot add additional type coercions to Union types");
ffbbf459 163 }
164 else{
165 $self->{_compiled_type_coercion} = sub {
166 my($thing) = @_;\r
167 foreach my $pair (@{$coercions}) {\r
168 #my ($constraint, $converter) = @$pair;\r
169 if ($pair->[0]->check($thing)) {\r
170 local $_ = $thing;
171 return $pair->[1]->($thing);
172 }\r
173 }\r
174 return $thing;\r
175 };
176 }
177 return;
178}
179
feb0e21b 180sub check {
181 my $self = shift;
ffbbf459 182 return $self->_compiled_type_constraint->(@_);
183}
184
185sub coerce {
186 my $self = shift;
ffbbf459 187
188 return $_[0] if $self->_compiled_type_constraint->(@_);
189
93540011 190 my $coercion = $self->_compiled_type_coercion;
191 return $coercion ? $coercion->(@_) : $_[0];
feb0e21b 192}
193
194sub get_message {
195 my ($self, $value) = @_;
196 if ( my $msg = $self->message ) {
197 local $_ = $value;
198 return $msg->($value);
199 }
200 else {
201 $value = ( defined $value ? overload::StrVal($value) : 'undef' );
202 return "Validation failed for '$self' failed with value $value";
203 }
204}
205
206sub is_a_type_of{
207 my($self, $other) = @_;
208
209 # ->is_a_type_of('__ANON__') is always false
ca352580 210 return 0 if !ref($other) && $other eq '__ANON__';
feb0e21b 211
212 (my $other_name = $other) =~ s/\s+//g;
213
214 return 1 if $self->name eq $other_name;
215
216 if(exists $self->{type_constraints}){ # union
217 foreach my $type(@{$self->{type_constraints}}){
218 return 1 if $type->name eq $other_name;
219 }
220 }
221
222 for(my $parent = $self->parent; defined $parent; $parent = $parent->parent){
223 return 1 if $parent->name eq $other_name;
224 }
225
226 return 0;
227}
228
b4d791ba 229# See also Moose::Meta::TypeConstraint::Parameterizable
230sub parameterize{
231 my($self, $param, $name) = @_;
232
233 if(!ref $param){
234 require Mouse::Util::TypeConstraints;
235 $param = Mouse::Util::TypeConstraints::find_or_create_isa_type_constraint($param);
236 }
237
238 $name ||= sprintf '%s[%s]', $self->name, $param->name;
239
240 my $generator = $self->{constraint_generator}
241 || Carp::confess("The $name constraint cannot be used, because $param doesn't subtype from a parameterizable type");
242
243 return Mouse::Meta::TypeConstraint->new(
5a363f78 244 name => $name,
245 parent => $self,
246 parameter => $param,
247 constraint => $generator->($param), # must be 'constraint', not 'optimized'
b4d791ba 248
5a363f78 249 type => 'Parameterized',
b4d791ba 250 );
251}
feb0e21b 252
684db121 2531;
254__END__
255
256=head1 NAME
257
1820fffe 258Mouse::Meta::TypeConstraint - The Mouse Type Constraint metaclass
684db121 259
a25ca8d6 260=head1 VERSION
261
2b68f76d 262This document describes Mouse version 0.40_03
a25ca8d6 263
684db121 264=head1 DESCRIPTION
265
266For the most part, the only time you will ever encounter an
267instance of this class is if you are doing some serious deep
268introspection. This API should not be considered final, but
269it is B<highly unlikely> that this will matter to a regular
270Mouse user.
271
272Don't use this.
273
274=head1 METHODS
275
276=over 4
277
278=item B<new>
279
280=item B<name>
281
282=back
283
1820fffe 284=head1 SEE ALSO
285
286L<Moose::Meta::TypeConstraint>
287
684db121 288=cut
289