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