Rename a too-long-length name
[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
5a592ad7 11 if($args{parent}) {
12 %args = (%{$args{parent}}, %args);
13 # a child type must not inherit 'compiled_type_constraint'
14 # and 'hand_optimized_type_constraint' from the parent
15 delete $args{compiled_type_constraint};
16 delete $args{hand_optimized_type_constraint};
17 }
18
19 my $check;
3b89ea91 20
5a592ad7 21 if($check = delete $args{optimized}) {
3b89ea91 22 $args{hand_optimized_type_constraint} = $check;
23 $args{compiled_type_constraint} = $check;
24 }
5a592ad7 25 elsif(my $param = $args{type_parameter}) {
26 my $generator = $args{constraint_generator}
27 || $class->throw_error("The $args{name} constraint cannot be used,"
28 . " because $param doesn't subtype from a parameterizable type");
29 # it must be 'constraint'
30 $check = $args{constraint} = $generator->($param);
31 }
32 else {
33 $check = $args{constraint};
34 }
f5ee065f 35
f5ee065f 36 if(defined($check) && ref($check) ne 'CODE'){
3a29a080 37 $class->throw_error(
38 "Constraint for $args{name} is not a CODE reference");
f5ee065f 39 }
40
41 my $self = bless \%args, $class;
3a29a080 42 $self->compile_type_constraint()
5a592ad7 43 if !$args{hand_optimized_type_constraint};
f5ee065f 44
5a592ad7 45 if($args{type_constraints}) {
46 $self->_compile_union_type_coercion();
47 }
f5ee065f 48 return $self;
49}
50
51sub create_child_type{
52 my $self = shift;
e98220ab 53 return ref($self)->new(
3b89ea91 54 @_,
5a592ad7 55 # and inherits other attributes from the parent
3b89ea91 56 parent => $self,
e98220ab 57 );
684db121 58}
59
4c99ea95 60sub name;
61sub parent;
62sub message;
63sub has_coercion;
2b82d75d 64
c7576321 65sub check;
66
2b82d75d 67sub type_parameter;
68sub __is_parameterized;
69
4c99ea95 70sub _compiled_type_constraint;
71sub _compiled_type_coercion;
72
73sub compile_type_constraint;
74
c7576321 75
3a29a080 76sub _add_type_coercions { # ($self, @pairs)
ffbbf459 77 my $self = shift;
78
cc131889 79 my $coercions = ($self->{coercion_map} ||= []);
ffbbf459 80 my %has = map{ $_->[0] => undef } @{$coercions};
81
82 for(my $i = 0; $i < @_; $i++){
83 my $from = $_[ $i];
84 my $action = $_[++$i];
85
86 if(exists $has{$from}){
c7576321 87 $self->throw_error("A coercion action already exists for '$from'");
ffbbf459 88 }
89
90 my $type = Mouse::Util::TypeConstraints::find_or_parse_type_constraint($from)
3a29a080 91 or $self->throw_error(
92 "Could not find the type constraint ($from) to coerce from");
ffbbf459 93
94 push @{$coercions}, [ $type => $action ];
95 }
96
97 # compile
98 if(exists $self->{type_constraints}){ # union type
3a29a080 99 $self->throw_error(
100 "Cannot add additional type coercions to Union types");
ffbbf459 101 }
102 else{
536e3c33 103 $self->_compile_type_coercion();
104 }
105 return;
106}
107
108sub _compile_type_coercion {
109 my($self) = @_;
110
cc131889 111 my @coercions = @{$self->{coercion_map}};
536e3c33 112
113 $self->{_compiled_type_coercion} = sub {
114 my($thing) = @_;
115 foreach my $pair (@coercions) {
116 #my ($constraint, $converter) = @$pair;
117 if ($pair->[0]->check($thing)) {
118 local $_ = $thing;
119 return $pair->[1]->($thing);
120 }
121 }
122 return $thing;
123 };
124 return;
125}
126
127sub _compile_union_type_coercion {
128 my($self) = @_;
129
130 my @coercions;
131 foreach my $type(@{$self->{type_constraints}}){
132 if($type->has_coercion){
133 push @coercions, $type;
134 }
135 }
136 if(@coercions){
ffbbf459 137 $self->{_compiled_type_coercion} = sub {
536e3c33 138 my($thing) = @_;
139 foreach my $type(@coercions){
140 my $value = $type->coerce($thing);
141 return $value if $self->check($value);
142 }
143 return $thing;
ffbbf459 144 };
145 }
146 return;
147}
148
ffbbf459 149sub coerce {
150 my $self = shift;
ffbbf459 151
de0d4152 152 my $coercion = $self->_compiled_type_coercion;
153 if(!$coercion){
c7576321 154 $self->throw_error("Cannot coerce without a type coercion");
de0d4152 155 }
156
c7576321 157 return $_[0] if $self->check(@_);
ffbbf459 158
de0d4152 159 return $coercion->(@_);
feb0e21b 160}
161
162sub get_message {
163 my ($self, $value) = @_;
164 if ( my $msg = $self->message ) {
165 local $_ = $value;
166 return $msg->($value);
167 }
168 else {
6e647cac 169 if(not defined $value) {
170 $value = 'undef';
171 }
172 elsif( ref($value) && defined(&overload::StrVal) ) {
173 $value = overload::StrVal($value);
174 }
537873b0 175 return "Validation failed for '$self' with value $value";
feb0e21b 176 }
177}
178
179sub is_a_type_of{
180 my($self, $other) = @_;
181
182 # ->is_a_type_of('__ANON__') is always false
ca352580 183 return 0 if !ref($other) && $other eq '__ANON__';
feb0e21b 184
185 (my $other_name = $other) =~ s/\s+//g;
186
187 return 1 if $self->name eq $other_name;
188
189 if(exists $self->{type_constraints}){ # union
3a29a080 190 foreach my $type(@{$self->{type_constraints}}) {
feb0e21b 191 return 1 if $type->name eq $other_name;
192 }
193 }
194
3a29a080 195 for(my $p = $self->parent; defined $p; $p = $p->parent) {
196 return 1 if $p->name eq $other_name;
feb0e21b 197 }
198
199 return 0;
200}
201
b4d791ba 202# See also Moose::Meta::TypeConstraint::Parameterizable
203sub parameterize{
204 my($self, $param, $name) = @_;
205
206 if(!ref $param){
207 require Mouse::Util::TypeConstraints;
208 $param = Mouse::Util::TypeConstraints::find_or_create_isa_type_constraint($param);
209 }
210
211 $name ||= sprintf '%s[%s]', $self->name, $param->name;
b4d791ba 212 return Mouse::Meta::TypeConstraint->new(
fc83f4cf 213 name => $name,
214 parent => $self,
215 type_parameter => $param,
b4d791ba 216 );
217}
feb0e21b 218
78508064 219sub assert_valid {
220 my ($self, $value) = @_;
221
c7576321 222 if(!$self->check($value)){
223 $self->throw_error($self->get_message($value));
78508064 224 }
225 return 1;
226}
227
6e647cac 228sub _as_string { $_[0]->name } # overload ""
229sub _identity { Scalar::Util::refaddr($_[0]) } # overload 0+
230
231sub _unite { # overload infix:<|>
232 my($lhs, $rhs) = @_;
233 require Mouse::Util::TypeConstraints;
234 return Mouse::Util::TypeConstraints::find_or_parse_type_constraint(
235 " $lhs | $rhs",
236 );
237}
238
684db121 2391;
240__END__
241
242=head1 NAME
243
1820fffe 244Mouse::Meta::TypeConstraint - The Mouse Type Constraint metaclass
684db121 245
a25ca8d6 246=head1 VERSION
247
43c1bb1a 248This document describes Mouse version 0.71
a25ca8d6 249
684db121 250=head1 DESCRIPTION
251
3a29a080 252This class represents a type constraint, including built-in
253type constraints, union type constraints, parameterizable/
254parameterized type constraints, as well as custom type
255constraints
684db121 256
257=head1 METHODS
258
ba05bb84 259=over
3a29a080 260
261=item C<< Mouse::Meta::TypeConstraint->new(%options) >>
262
263=item C<< $constraint->name >>
264
265=item C<< $constraint->parent >>
266
267=item C<< $constraint->constraint >>
268
269=item C<< $constraint->has_coercion >>
270
271=item C<< $constraint->message >>
272
273=item C<< $constraint->is_a_subtype_of($name or $object) >>
274
275=item C<< $constraint->coerce($value) >>
276
277=item C<< $constraint->check($value) >>
278
279=item C<< $constraint->assert_valid($value) >>
684db121 280
3a29a080 281=item C<< $constraint->get_message($value) >>
684db121 282
3a29a080 283=item C<< $constraint->create_child_type(%options) >>
684db121 284
285=back
286
1820fffe 287=head1 SEE ALSO
288
289L<Moose::Meta::TypeConstraint>
290
684db121 291=cut
292