1 package Mouse::Util::TypeConstraints;
7 use Scalar::Util qw/blessed looks_like_number openhandle/;
10 as where message from via type subtype coerce class_type role_type enum
35 no warnings 'uninitialized';
40 !defined($_[0]) || $_[0] eq "" || "$_[0]" eq '1' || "$_[0]" eq '0'
42 Undef => sub { !defined($_[0]) },
43 Defined => sub { defined($_[0]) },
44 Value => sub { defined($_[0]) && !ref($_[0]) },
45 Num => sub { !ref($_[0]) && looks_like_number($_[0]) },
46 Int => sub { defined($_[0]) && !ref($_[0]) && $_[0] =~ /^-?[0-9]+$/ },
47 Str => sub { defined($_[0]) && !ref($_[0]) },
48 ClassName => sub { Mouse::is_class_loaded($_[0]) },
49 Ref => sub { ref($_[0]) },
51 ScalarRef => sub { ref($_[0]) eq 'SCALAR' },
52 ArrayRef => sub { ref($_[0]) eq 'ARRAY' },
53 HashRef => sub { ref($_[0]) eq 'HASH' },
54 CodeRef => sub { ref($_[0]) eq 'CODE' },
55 RegexpRef => sub { ref($_[0]) eq 'Regexp' },
56 GlobRef => sub { ref($_[0]) eq 'GLOB' },
59 ref($_[0]) eq 'GLOB' && openhandle($_[0])
61 blessed($_[0]) && $_[0]->isa("IO::Handle")
64 Object => sub { blessed($_[0]) && blessed($_[0]) ne 'Regexp' },
66 foreach my $code (values %TYPE) {
67 bless $code, 'Mouse::Meta::TypeConstraint';
70 sub optimized_constraints { \%TYPE }
71 my @TYPE_KEYS = keys %TYPE;
72 sub list_all_builtin_type_constraints { @TYPE_KEYS }
74 @TYPE_SOURCE{@TYPE_KEYS} = (__PACKAGE__) x @TYPE_KEYS;
79 my($name, %conf) = @_;
80 if ($TYPE{$name} && $TYPE_SOURCE{$name} ne $pkg) {
81 Carp::croak "The type constraint '$name' has already been created in $TYPE_SOURCE{$name} and cannot be created again in $pkg";
83 my $constraint = $conf{where} || do {
84 my $as = delete $conf{as} || 'Any';
85 if (! exists $TYPE{$as}) {
86 $TYPE{$as} = _build_type_constraint($as);
91 $TYPE_SOURCE{$name} = $pkg;
92 $TYPE{$name} = sub { local $_ = $_[0]; $constraint->($_[0]) };
97 my($name, %conf) = @_;
98 if ($TYPE{$name} && $TYPE_SOURCE{$name} ne $pkg) {
99 Carp::croak "The type constraint '$name' has already been created in $TYPE_SOURCE{$name} and cannot be created again in $pkg";
101 my $constraint = $conf{where};
102 my $as_constraint = find_or_create_isa_type_constraint($conf{as} || 'Any');
104 $TYPE_SOURCE{$name} = $pkg;
105 $TYPE{$name} = $constraint ?
108 $as_constraint->($_[0]) && $constraint->($_[0])
112 $as_constraint->($_[0]);
120 my($name, %conf) = @_;
122 Carp::croak "Cannot find type '$name', perhaps you forgot to load it."
125 unless ($COERCE{$name}) {
127 $COERCE_KEYS{$name} = [];
129 while (my($type, $code) = each %conf) {
130 Carp::croak "A coercion action already exists for '$type'"
131 if $COERCE{$name}->{$type};
133 if (! $TYPE{$type}) {
134 # looks parameterized
135 if ($type =~ /^[^\[]+\[.+\]$/) {
136 _build_type_constraint($type);
138 Carp::croak "Could not find the type constraint ($type) to coerce from"
142 push @{ $COERCE_KEYS{$name} }, $type;
143 $COERCE{$name}->{$type} = $code;
148 my($name, $conf) = @_;
149 if ($conf && $conf->{class}) {
150 # No, you're using this wrong
151 warn "class_type() should be class_type(ClassName). Perhaps you're looking for subtype $name => as '$conf->{class}'?";
152 subtype($name, as => $conf->{class});
155 $name => where => sub { $_->isa($name) }
161 my($name, $conf) = @_;
162 my $role = $conf->{role};
164 $name => where => sub {
165 return unless defined $_ && ref($_) && $_->isa('Mouse::Object');
166 $_->meta->does_role($role);
171 sub typecast_constraints {
172 my($class, $pkg, $type_constraint, $types, $value) = @_;
175 for my $type (ref($types) eq 'ARRAY' ? @{ $types } : ( $types )) {
176 next unless $COERCE{$type};
177 for my $coerce_type (@{ $COERCE_KEYS{$type}}) {
179 next unless $TYPE{$coerce_type}->($value);
181 $_ = $COERCE{$type}->{$coerce_type}->($value);
182 return $_ if $type_constraint->($_);
190 # enum ['small', 'medium', 'large']
191 if (ref($_[0]) eq 'ARRAY') {
192 my @elements = @{ shift @_ };
194 my $name = 'Mouse::Util::TypeConstaints::Enum::Serial::'
196 enum($name, @elements);
200 # enum size => 'small', 'medium', 'large'
202 my %is_valid = map { $_ => 1 } @_;
205 $name => where => sub { $is_valid{$_} }
209 sub _build_type_constraint {
213 if ($spec =~ /^([^\[]+)\[(.+)\]$/) {
218 if ($constraint eq 'Maybe') {
219 $parent = _build_type_constraint('Undef');
221 $parent = _build_type_constraint($constraint);
223 my $child = _build_type_constraint($param);
224 if ($constraint eq 'ArrayRef') {
226 "#line " . __LINE__ . ' "' . __FILE__ . "\"\n" .
228 " if (\$parent->(\$_[0])) {\n" .
229 " foreach my \$e (\@{\$_[0]}) {\n" .
230 " return () unless \$child->(\$e);\n" .
237 $code = eval $code_str or Carp::confess("Failed to generate inline type constraint: $@");
238 } elsif ($constraint eq 'HashRef') {
240 "#line " . __LINE__ . ' "' . __FILE__ . "\"\n" .
242 " if (\$parent->(\$_[0])) {\n" .
243 " foreach my \$e (values \%{\$_[0]}) {\n" .
244 " return () unless \$child->(\$e);\n" .
251 $code = eval $code_str or Carp::confess($@);
252 } elsif ($constraint eq 'Maybe') {
254 "#line " . __LINE__ . ' "' . __FILE__ . "\"\n" .
256 " return \$child->(\$_[0]) || \$parent->(\$_[0]);\n" .
259 $code = eval $code_str or Carp::confess($@);
261 Carp::confess("Support for parameterized types other than ArrayRef or HashRef is not implemented yet");
263 $TYPE{$spec} = $code;
265 $code = $TYPE{ $spec };
268 "#line " . __LINE__ . ' "' . __FILE__ . "\"\n" .
270 " Scalar::Util::blessed(\$_[0]) && \$_[0]->isa('$spec');\n" .
273 $code = eval $code_str or Carp::confess($@);
274 $TYPE{$spec} = bless $code, 'Mouse::Meta::TypeConstraint';
277 return bless $code, 'Mouse::Meta::TypeConstraint';
280 sub find_type_constraint {
281 my $type_constraint = shift;
282 return $TYPE{$type_constraint};
285 sub find_or_create_isa_type_constraint {
286 my $type_constraint = shift;
290 $type_constraint =~ s/\s+//g;
291 my @type_constraints = split /\|/, $type_constraint;
292 if (@type_constraints == 1) {
293 $code = $TYPE{$type_constraints[0]} ||
294 _build_type_constraint($type_constraints[0]);
296 my @code_list = map {
297 $TYPE{$_} || _build_type_constraint($_)
301 for my $code (@code_list) {
302 return 1 if $code->($_[0]);
305 }, 'Mouse::Meta::TypeConstraint';
310 package # Hide from pause
311 Mouse::Meta::TypeConstraint;
324 Mouse::Util::TypeConstraints - simple type constraints
328 =head2 optimized_constraints -> HashRef[CODE]
330 Returns the simple type constraints that Mouse understands.
336 =item B<subtype 'Name' => as 'Parent' => where { } ...>
338 =item B<subtype as 'Parent' => where { } ...>
340 =item B<class_type ($class, ?$options)>
342 =item B<role_type ($role, ?$options)>
344 =item B<enum (\@values)>