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