Move overload stuff to 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
184f8f53 3use Scalar::Util ();
9c85e9dc 4
684db121 5sub new {
184f8f53 6 my $class = shift;
7 my %args = @_ == 1 ? %{$_[0]} : @_;
f5ee065f 8
9 $args{name} = '__ANON__' if !defined $args{name};
684db121 10
3b89ea91 11 my $check = delete $args{optimized};
12
3b89ea91 13 if($check){
14 $args{hand_optimized_type_constraint} = $check;
15 $args{compiled_type_constraint} = $check;
16 }
17
18 $check = $args{constraint};
f5ee065f 19
f5ee065f 20 if(defined($check) && ref($check) ne 'CODE'){
c7576321 21 $class->throw_error("Constraint for $args{name} is not a CODE reference");
f5ee065f 22 }
23
24 my $self = bless \%args, $class;
3b89ea91 25 $self->compile_type_constraint() if !$self->{hand_optimized_type_constraint};
f5ee065f 26
536e3c33 27 $self->_compile_union_type_coercion() if $self->{type_constraints};
f5ee065f 28 return $self;
29}
30
31sub create_child_type{
32 my $self = shift;
e98220ab 33 return ref($self)->new(
3b89ea91 34 # a child inherits its parent's attributes
35 %{$self},
36
37 # but does not inherit 'compiled_type_constraint' and 'hand_optimized_type_constraint'
38 compiled_type_constraint => undef,
39 hand_optimized_type_constraint => undef,
40
41 # and is given child-specific args, of course.
42 @_,
43
44 # and its parent
45 parent => $self,
e98220ab 46 );
684db121 47}
48
4c99ea95 49sub name;
50sub parent;
51sub message;
52sub has_coercion;
2b82d75d 53
c7576321 54sub check;
55
2b82d75d 56sub type_parameter;
57sub __is_parameterized;
58
4c99ea95 59sub _compiled_type_constraint;
60sub _compiled_type_coercion;
61
62sub compile_type_constraint;
63
c7576321 64
ffbbf459 65sub _add_type_coercions{
66 my $self = shift;
67
cc131889 68 my $coercions = ($self->{coercion_map} ||= []);
ffbbf459 69 my %has = map{ $_->[0] => undef } @{$coercions};
70
71 for(my $i = 0; $i < @_; $i++){
72 my $from = $_[ $i];
73 my $action = $_[++$i];
74
75 if(exists $has{$from}){
c7576321 76 $self->throw_error("A coercion action already exists for '$from'");
ffbbf459 77 }
78
79 my $type = Mouse::Util::TypeConstraints::find_or_parse_type_constraint($from)
c7576321 80 or $self->throw_error("Could not find the type constraint ($from) to coerce from");
ffbbf459 81
82 push @{$coercions}, [ $type => $action ];
83 }
84
85 # compile
86 if(exists $self->{type_constraints}){ # union type
c7576321 87 $self->throw_error("Cannot add additional type coercions to Union types");
ffbbf459 88 }
89 else{
536e3c33 90 $self->_compile_type_coercion();
91 }
92 return;
93}
94
95sub _compile_type_coercion {
96 my($self) = @_;
97
cc131889 98 my @coercions = @{$self->{coercion_map}};
536e3c33 99
100 $self->{_compiled_type_coercion} = sub {
101 my($thing) = @_;
102 foreach my $pair (@coercions) {
103 #my ($constraint, $converter) = @$pair;
104 if ($pair->[0]->check($thing)) {
105 local $_ = $thing;
106 return $pair->[1]->($thing);
107 }
108 }
109 return $thing;
110 };
111 return;
112}
113
114sub _compile_union_type_coercion {
115 my($self) = @_;
116
117 my @coercions;
118 foreach my $type(@{$self->{type_constraints}}){
119 if($type->has_coercion){
120 push @coercions, $type;
121 }
122 }
123 if(@coercions){
ffbbf459 124 $self->{_compiled_type_coercion} = sub {
536e3c33 125 my($thing) = @_;
126 foreach my $type(@coercions){
127 my $value = $type->coerce($thing);
128 return $value if $self->check($value);
129 }
130 return $thing;
ffbbf459 131 };
132 }
133 return;
134}
135
ffbbf459 136sub coerce {
137 my $self = shift;
ffbbf459 138
de0d4152 139 my $coercion = $self->_compiled_type_coercion;
140 if(!$coercion){
c7576321 141 $self->throw_error("Cannot coerce without a type coercion");
de0d4152 142 }
143
c7576321 144 return $_[0] if $self->check(@_);
ffbbf459 145
de0d4152 146 return $coercion->(@_);
feb0e21b 147}
148
149sub get_message {
150 my ($self, $value) = @_;
151 if ( my $msg = $self->message ) {
152 local $_ = $value;
153 return $msg->($value);
154 }
155 else {
6e647cac 156 if(not defined $value) {
157 $value = 'undef';
158 }
159 elsif( ref($value) && defined(&overload::StrVal) ) {
160 $value = overload::StrVal($value);
161 }
537873b0 162 return "Validation failed for '$self' with value $value";
feb0e21b 163 }
164}
165
166sub is_a_type_of{
167 my($self, $other) = @_;
168
169 # ->is_a_type_of('__ANON__') is always false
ca352580 170 return 0 if !ref($other) && $other eq '__ANON__';
feb0e21b 171
172 (my $other_name = $other) =~ s/\s+//g;
173
174 return 1 if $self->name eq $other_name;
175
176 if(exists $self->{type_constraints}){ # union
177 foreach my $type(@{$self->{type_constraints}}){
178 return 1 if $type->name eq $other_name;
179 }
180 }
181
182 for(my $parent = $self->parent; defined $parent; $parent = $parent->parent){
183 return 1 if $parent->name eq $other_name;
184 }
185
186 return 0;
187}
188
b4d791ba 189# See also Moose::Meta::TypeConstraint::Parameterizable
190sub parameterize{
191 my($self, $param, $name) = @_;
192
193 if(!ref $param){
194 require Mouse::Util::TypeConstraints;
195 $param = Mouse::Util::TypeConstraints::find_or_create_isa_type_constraint($param);
196 }
197
198 $name ||= sprintf '%s[%s]', $self->name, $param->name;
199
200 my $generator = $self->{constraint_generator}
c7576321 201 || $self->throw_error("The $name constraint cannot be used, because $param doesn't subtype from a parameterizable type");
b4d791ba 202
203 return Mouse::Meta::TypeConstraint->new(
fc83f4cf 204 name => $name,
205 parent => $self,
206 type_parameter => $param,
207 constraint => $generator->($param), # must be 'constraint', not 'optimized'
b4d791ba 208 );
209}
feb0e21b 210
78508064 211sub assert_valid {
212 my ($self, $value) = @_;
213
c7576321 214 if(!$self->check($value)){
215 $self->throw_error($self->get_message($value));
78508064 216 }
217 return 1;
218}
219
6e647cac 220sub _as_string { $_[0]->name } # overload ""
221sub _identity { Scalar::Util::refaddr($_[0]) } # overload 0+
222
223sub _unite { # overload infix:<|>
224 my($lhs, $rhs) = @_;
225 require Mouse::Util::TypeConstraints;
226 return Mouse::Util::TypeConstraints::find_or_parse_type_constraint(
227 " $lhs | $rhs",
228 );
229}
230
c7576321 231sub throw_error {
232 require Mouse::Meta::Module;
233 goto &Mouse::Meta::Module::throw_error;
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
86eb0b5e 245This document describes Mouse version 0.70
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