fix a couple of 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             Scalar::Util::openhandle($_) || ( blessed($_) && $_->isa("IO::Handle") );
106         }
107         => inline_as {
108             '(ref(' . $_[1] . ') eq "GLOB" '
109               . '&& Scalar::Util::openhandle(' . $_[1] . ')) '
110        . '|| (Scalar::Util::blessed(' . $_[1] . ') '
111               . '&& ' . $_[1] . '->isa("IO::Handle"))'
112         };
113
114     subtype 'Object'
115         => as 'Ref'
116         => where { blessed($_) }
117         => inline_as { 'Scalar::Util::blessed(' . $_[1] . ')' };
118
119     # This type is deprecated.
120     subtype 'Role'
121         => as 'Object'
122         => where { $_->can('does') };
123
124     subtype 'ClassName'
125         => as 'Str'
126         => where { Class::MOP::is_class_loaded($_) }
127         => inline_as { 'Class::MOP::is_class_loaded(' . $_[1] . ')' };
128
129     subtype 'RoleName'
130         => as 'ClassName'
131         => where {
132             (Class::MOP::class_of($_) || return)->isa('Moose::Meta::Role');
133         }
134         => inline_as {
135             'Class::MOP::is_class_loaded(' . $_[1] . ') '
136               . '&& (Class::MOP::class_of(' . $_[1] . ') || return)->isa('
137                   . '"Moose::Meta::Role"'
138               . ')'
139         };
140
141     $registry->add_type_constraint(
142         Moose::Meta::TypeConstraint::Parameterizable->new(
143             name               => 'ScalarRef',
144             package_defined_in => __PACKAGE__,
145             parent =>
146                 Moose::Util::TypeConstraints::find_type_constraint('Ref'),
147             constraint => sub { ref($_) eq 'SCALAR' || ref($_) eq 'REF' },
148             constraint_generator => sub {
149                 my $type_parameter = shift;
150                 my $check = $type_parameter->_compiled_type_constraint;
151                 return sub {
152                     return $check->( ${$_} );
153                 };
154             },
155             inlined => sub {
156                 'ref(' . $_[1] . ') eq "SCALAR" '
157                   . '|| ref(' . $_[1] . ') eq "REF"'
158             },
159             inline_generator => sub {
160                 my $self           = shift;
161                 my $type_parameter = shift;
162                 my $val            = shift;
163                 '(ref(' . $val . ') eq "SCALAR" || ref(' . $val . ') eq "REF") '
164                   . '&& ' . $type_parameter->_inline_check('${(' . $val . ')}')
165             },
166         )
167     );
168
169     $registry->add_type_constraint(
170         Moose::Meta::TypeConstraint::Parameterizable->new(
171             name               => 'ArrayRef',
172             package_defined_in => __PACKAGE__,
173             parent =>
174                 Moose::Util::TypeConstraints::find_type_constraint('Ref'),
175             constraint => sub { ref($_) eq 'ARRAY' },
176             constraint_generator => sub {
177                 my $type_parameter = shift;
178                 my $check = $type_parameter->_compiled_type_constraint;
179                 return sub {
180                     foreach my $x (@$_) {
181                         ( $check->($x) ) || return;
182                     }
183                     1;
184                     }
185             },
186             inlined          => sub { 'ref(' . $_[1] . ') eq "ARRAY"' },
187             inline_generator => sub {
188                 my $self           = shift;
189                 my $type_parameter = shift;
190                 my $val            = shift;
191                 'ref(' . $val . ') eq "ARRAY" '
192                   . '&& &List::MoreUtils::all('
193                       . 'sub { ' . $type_parameter->_inline_check('$_') . ' }, '
194                       . '@{' . $val . '}'
195                   . ')'
196             },
197         )
198     );
199
200     $registry->add_type_constraint(
201         Moose::Meta::TypeConstraint::Parameterizable->new(
202             name               => 'HashRef',
203             package_defined_in => __PACKAGE__,
204             parent =>
205                 Moose::Util::TypeConstraints::find_type_constraint('Ref'),
206             constraint => sub { ref($_) eq 'HASH' },
207             constraint_generator => sub {
208                 my $type_parameter = shift;
209                 my $check = $type_parameter->_compiled_type_constraint;
210                 return sub {
211                     foreach my $x ( values %$_ ) {
212                         ( $check->($x) ) || return;
213                     }
214                     1;
215                     }
216             },
217             inlined          => sub { 'ref(' . $_[1] . ') eq "HASH"' },
218             inline_generator => sub {
219                 my $self           = shift;
220                 my $type_parameter = shift;
221                 my $val            = shift;
222                 'ref(' . $val . ') eq "HASH" '
223                   . '&& &List::MoreUtils::all('
224                       . 'sub { ' . $type_parameter->_inline_check('$_') . ' }, '
225                       . 'values %{' . $val . '}'
226                   . ')'
227             },
228         )
229     );
230
231     $registry->add_type_constraint(
232         Moose::Meta::TypeConstraint::Parameterizable->new(
233             name               => 'Maybe',
234             package_defined_in => __PACKAGE__,
235             parent =>
236                 Moose::Util::TypeConstraints::find_type_constraint('Item'),
237             constraint           => sub {1},
238             constraint_generator => sub {
239                 my $type_parameter = shift;
240                 my $check = $type_parameter->_compiled_type_constraint;
241                 return sub {
242                     return 1 if not( defined($_) ) || $check->($_);
243                     return;
244                     }
245             },
246             inlined          => sub {'1'},
247             inline_generator => sub {
248                 my $self           = shift;
249                 my $type_parameter = shift;
250                 my $val            = shift;
251                 '!defined(' . $val . ') '
252                   . '|| (' . $type_parameter->_inline_check($val) . ')'
253             },
254         )
255     );
256 }
257
258 1;