one more fix for unoptimized constraints
[gitmo/Moose.git] / lib / Moose / Util / TypeConstraints / Builtins.pm
1 package Moose::Util::TypeConstraints::Builtins;
2
3 use strict;
4 use warnings;
5
6 use List::MoreUtils ();
7 use Scalar::Util qw( blessed looks_like_number reftype );
8
9 sub type { goto &Moose::Util::TypeConstraints::type }
10 sub subtype { goto &Moose::Util::TypeConstraints::subtype }
11 sub as { goto &Moose::Util::TypeConstraints::as }
12 sub where (&) { goto &Moose::Util::TypeConstraints::where }
13 sub optimize_as (&) { goto &Moose::Util::TypeConstraints::optimize_as }
14 sub inline_as (&) { goto &Moose::Util::TypeConstraints::inline_as }
15
16 sub define_builtins {
17     my $registry = shift;
18
19     type 'Any'    # meta-type including all
20         => where {1}
21         => inline_as { '1' };
22
23     subtype 'Item'  # base-type
24         => as 'Any'
25         => inline_as { '1' };
26
27     subtype 'Undef'
28         => as 'Item'
29         => where { !defined($_) }
30         => inline_as { '!defined(' . $_[1] . ')' };
31
32     subtype 'Defined'
33         => as 'Item'
34         => where { defined($_) }
35         => inline_as { 'defined(' . $_[1] . ')' };
36
37     subtype 'Bool'
38         => as 'Item'
39         => where { !defined($_) || $_ eq "" || "$_" eq '1' || "$_" eq '0' }
40         => inline_as {
41             '!defined(' . $_[1] . ') '
42               . '|| ' . $_[1] . ' eq "" '
43               . '|| "' . $_[1] . '" eq "1" '
44               . '|| "' . $_[1] . '" eq "0"'
45         };
46
47     subtype 'Value'
48         => as 'Defined'
49         => where { !ref($_) }
50         => inline_as { 'defined(' . $_[1] . ') && !ref(' . $_[1] . ')' };
51
52     subtype 'Ref'
53         => as 'Defined'
54         => where { ref($_) }
55         => inline_as { 'ref(' . $_[1] . ')' };
56
57     subtype 'Str'
58         => as 'Value'
59         => where { ref(\$_) eq 'SCALAR' || ref(\(my $val = $_)) eq 'SCALAR' }
60         => inline_as {
61             'defined(' . $_[1] . ') '
62               . '&& (ref(\\' . $_[1] . ') eq "SCALAR"'
63                . '|| ref(\\(my $val = ' . $_[1] . ')) eq "SCALAR")'
64         };
65
66     subtype 'Num'
67         => as 'Str'
68         => where { Scalar::Util::looks_like_number($_) }
69         => inline_as {
70             '!ref(' . $_[1] . ') '
71               . '&& Scalar::Util::looks_like_number(' . $_[1] . ')'
72         };
73
74     subtype 'Int'
75         => as 'Num'
76         => where { (my $val = $_) =~ /\A-?[0-9]+\z/ }
77         => inline_as {
78             'defined(' . $_[1] . ') '
79               . '&& !ref(' . $_[1] . ') '
80               . '&& (my $val = ' . $_[1] . ') =~ /\A-?[0-9]+\z/'
81         };
82
83     subtype 'CodeRef'
84         => as 'Ref'
85         => where { ref($_) eq 'CODE' }
86         => inline_as { 'ref(' . $_[1] . ') eq "CODE"' };
87
88     subtype 'RegexpRef'
89         => as 'Ref'
90         => where( \&_RegexpRef )
91         => inline_as {
92             'Moose::Util::TypeConstraints::Builtins::_RegexpRef(' . $_[1] . ')'
93         };
94
95     subtype 'GlobRef'
96         => as 'Ref'
97         => where { ref($_) eq 'GLOB' }
98         => inline_as { 'ref(' . $_[1] . ') eq "GLOB"' };
99
100     # NOTE: scalar filehandles are GLOB refs, but a GLOB ref is not always a
101     # filehandle
102     subtype 'FileHandle'
103         => as 'Ref'
104         => where {
105             (ref($_) eq "GLOB" && Scalar::Util::openhandle($_))
106          || (blessed($_) && $_->isa("IO::Handle"));
107         }
108         => inline_as {
109             '(ref(' . $_[1] . ') eq "GLOB" '
110               . '&& Scalar::Util::openhandle(' . $_[1] . ')) '
111        . '|| (Scalar::Util::blessed(' . $_[1] . ') '
112               . '&& ' . $_[1] . '->isa("IO::Handle"))'
113         };
114
115     subtype 'Object'
116         => as 'Ref'
117         => where { blessed($_) }
118         => inline_as { 'Scalar::Util::blessed(' . $_[1] . ')' };
119
120     # This type is deprecated.
121     subtype 'Role'
122         => as 'Object'
123         => where { $_->can('does') };
124
125     subtype 'ClassName'
126         => as 'Str'
127         => where { Class::MOP::is_class_loaded($_) }
128         => inline_as { 'Class::MOP::is_class_loaded(' . $_[1] . ')' };
129
130     subtype 'RoleName'
131         => as 'ClassName'
132         => where {
133             (Class::MOP::class_of($_) || return)->isa('Moose::Meta::Role');
134         }
135         => inline_as {
136             'Class::MOP::is_class_loaded(' . $_[1] . ') '
137               . '&& (Class::MOP::class_of(' . $_[1] . ') || return)->isa('
138                   . '"Moose::Meta::Role"'
139               . ')'
140         };
141
142     $registry->add_type_constraint(
143         Moose::Meta::TypeConstraint::Parameterizable->new(
144             name               => 'ScalarRef',
145             package_defined_in => __PACKAGE__,
146             parent =>
147                 Moose::Util::TypeConstraints::find_type_constraint('Ref'),
148             constraint => sub { ref($_) eq 'SCALAR' || ref($_) eq 'REF' },
149             constraint_generator => sub {
150                 my $type_parameter = shift;
151                 my $check = $type_parameter->_compiled_type_constraint;
152                 return sub {
153                     return $check->( ${$_} );
154                 };
155             },
156             inlined => sub {
157                 'ref(' . $_[1] . ') eq "SCALAR" '
158                   . '|| ref(' . $_[1] . ') eq "REF"'
159             },
160             inline_generator => sub {
161                 my $self           = shift;
162                 my $type_parameter = shift;
163                 my $val            = shift;
164                 '(ref(' . $val . ') eq "SCALAR" || ref(' . $val . ') eq "REF") '
165                   . '&& ' . $type_parameter->_inline_check('${(' . $val . ')}')
166             },
167         )
168     );
169
170     $registry->add_type_constraint(
171         Moose::Meta::TypeConstraint::Parameterizable->new(
172             name               => 'ArrayRef',
173             package_defined_in => __PACKAGE__,
174             parent =>
175                 Moose::Util::TypeConstraints::find_type_constraint('Ref'),
176             constraint => sub { ref($_) eq 'ARRAY' },
177             constraint_generator => sub {
178                 my $type_parameter = shift;
179                 my $check = $type_parameter->_compiled_type_constraint;
180                 return sub {
181                     foreach my $x (@$_) {
182                         ( $check->($x) ) || return;
183                     }
184                     1;
185                     }
186             },
187             inlined          => sub { 'ref(' . $_[1] . ') eq "ARRAY"' },
188             inline_generator => sub {
189                 my $self           = shift;
190                 my $type_parameter = shift;
191                 my $val            = shift;
192                 'ref(' . $val . ') eq "ARRAY" '
193                   . '&& &List::MoreUtils::all('
194                       . 'sub { ' . $type_parameter->_inline_check('$_') . ' }, '
195                       . '@{' . $val . '}'
196                   . ')'
197             },
198         )
199     );
200
201     $registry->add_type_constraint(
202         Moose::Meta::TypeConstraint::Parameterizable->new(
203             name               => 'HashRef',
204             package_defined_in => __PACKAGE__,
205             parent =>
206                 Moose::Util::TypeConstraints::find_type_constraint('Ref'),
207             constraint => sub { ref($_) eq 'HASH' },
208             constraint_generator => sub {
209                 my $type_parameter = shift;
210                 my $check = $type_parameter->_compiled_type_constraint;
211                 return sub {
212                     foreach my $x ( values %$_ ) {
213                         ( $check->($x) ) || return;
214                     }
215                     1;
216                     }
217             },
218             inlined          => sub { 'ref(' . $_[1] . ') eq "HASH"' },
219             inline_generator => sub {
220                 my $self           = shift;
221                 my $type_parameter = shift;
222                 my $val            = shift;
223                 'ref(' . $val . ') eq "HASH" '
224                   . '&& &List::MoreUtils::all('
225                       . 'sub { ' . $type_parameter->_inline_check('$_') . ' }, '
226                       . 'values %{' . $val . '}'
227                   . ')'
228             },
229         )
230     );
231
232     $registry->add_type_constraint(
233         Moose::Meta::TypeConstraint::Parameterizable->new(
234             name               => 'Maybe',
235             package_defined_in => __PACKAGE__,
236             parent =>
237                 Moose::Util::TypeConstraints::find_type_constraint('Item'),
238             constraint           => sub {1},
239             constraint_generator => sub {
240                 my $type_parameter = shift;
241                 my $check = $type_parameter->_compiled_type_constraint;
242                 return sub {
243                     return 1 if not( defined($_) ) || $check->($_);
244                     return;
245                     }
246             },
247             inlined          => sub {'1'},
248             inline_generator => sub {
249                 my $self           = shift;
250                 my $type_parameter = shift;
251                 my $val            = shift;
252                 '!defined(' . $val . ') '
253                   . '|| (' . $type_parameter->_inline_check($val) . ')'
254             },
255         )
256     );
257 }
258
259 1;