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