package Mouse::Util::TypeConstraints;
use strict;
use warnings;
+use base 'Exporter';
use Carp ();
use Scalar::Util qw/blessed looks_like_number openhandle/;
-my %SUBTYPE;
+our @EXPORT = qw(
+ as where message from via type subtype coerce class_type role_type enum
+);
+
+my %TYPE;
+my %TYPE_SOURCE;
my %COERCE;
my %COERCE_KEYS;
-#find_type_constraint register_type_constraint
-sub import {
- my $class = shift;
- my %args = @_;
- my $caller = $args{callee} || caller(0);
-
- no strict 'refs';
- *{"$caller\::as"} = \&_as;
- *{"$caller\::where"} = \&_where;
- *{"$caller\::message"} = \&_message;
- *{"$caller\::from"} = \&_from;
- *{"$caller\::via"} = \&_via;
- *{"$caller\::subtype"} = \&_subtype;
- *{"$caller\::coerce"} = \&_coerce;
- *{"$caller\::class_type"} = \&_class_type;
- *{"$caller\::role_type"} = \&_role_type;
-}
-
-
-sub _as ($) {
+sub as ($) {
as => $_[0]
}
-sub _where (&) {
+sub where (&) {
where => $_[0]
}
-sub _message ($) {
+sub message (&) {
message => $_[0]
}
-sub _from { @_ }
-sub _via (&) {
+sub from { @_ }
+sub via (&) {
$_[0]
}
my $optimized_constraints_base;
{
no warnings 'uninitialized';
- %SUBTYPE = (
+ %TYPE = (
Any => sub { 1 },
Item => sub { 1 },
Bool => sub {
GlobRef => sub { ref($_) eq 'GLOB' },
FileHandle => sub {
- ref($_) eq 'GLOB'
- && openhandle($_)
+ ref($_) eq 'GLOB' && openhandle($_)
or
- blessed($_)
- && $_->isa("IO::Handle")
- },
+ blessed($_) && $_->isa("IO::Handle")
+ },
Object => sub { blessed($_) && blessed($_) ne 'Regexp' },
);
- sub optimized_constraints { \%SUBTYPE }
- my @SUBTYPE_KEYS = keys %SUBTYPE;
- sub list_all_builtin_type_constraints { @SUBTYPE_KEYS }
+ sub optimized_constraints { \%TYPE }
+ my @TYPE_KEYS = keys %TYPE;
+ sub list_all_builtin_type_constraints { @TYPE_KEYS }
+
+ @TYPE_SOURCE{@TYPE_KEYS} = (__PACKAGE__) x @TYPE_KEYS;
+}
+
+sub type {
+ my $pkg = caller(0);
+ my($name, %conf) = @_;
+ if ($TYPE{$name} && $TYPE_SOURCE{$name} ne $pkg) {
+ Carp::croak "The type constraint '$name' has already been created in $TYPE_SOURCE{$name} and cannot be created again in $pkg";
+ };
+ my $constraint = $conf{where} || do { $TYPE{delete $conf{as} || 'Any' } };
+
+ $TYPE_SOURCE{$name} = $pkg;
+ $TYPE{$name} = $constraint;
}
-sub _subtype {
+sub subtype {
my $pkg = caller(0);
my($name, %conf) = @_;
- if (my $type = $SUBTYPE{$name}) {
- Carp::croak "The type constraint '$name' has already been created, cannot be created again in $pkg";
+ if ($TYPE{$name} && $TYPE_SOURCE{$name} ne $pkg) {
+ Carp::croak "The type constraint '$name' has already been created in $TYPE_SOURCE{$name} and cannot be created again in $pkg";
};
- my $stuff = $conf{where} || do { $SUBTYPE{delete $conf{as} || 'Any' } };
- my $as = $conf{as} || '';
- if ($as = $SUBTYPE{$as}) {
- $SUBTYPE{$name} = sub { $as->($_) && $stuff->($_) };
+ my $constraint = $conf{where} || do { $TYPE{delete $conf{as} || 'Any' } };
+ my $as = $conf{as} || '';
+
+ $TYPE_SOURCE{$name} = $pkg;
+
+ if ($as = $TYPE{$as}) {
+ $TYPE{$name} = sub { $as->($_) && $constraint->($_) };
} else {
- $SUBTYPE{$name} = $stuff;
+ $TYPE{$name} = $constraint;
}
}
-sub _coerce {
+sub coerce {
my($name, %conf) = @_;
Carp::croak "Cannot find type '$name', perhaps you forgot to load it."
- unless $SUBTYPE{$name};
+ unless $TYPE{$name};
unless ($COERCE{$name}) {
$COERCE{$name} = {};
if $COERCE{$name}->{$type};
Carp::croak "Could not find the type constraint ($type) to coerce from"
- unless $SUBTYPE{$type};
+ unless $TYPE{$type};
push @{ $COERCE_KEYS{$name} }, $type;
$COERCE{$name}->{$type} = $code;
}
}
-sub _class_type {
+sub class_type {
my $pkg = caller(0);
my($name, $conf) = @_;
my $class = $conf->{class};
- Mouse::load_class($class);
- _subtype(
+ subtype(
$name => where => sub { $_->isa($class) }
);
}
-sub _role_type {
+sub role_type {
my($name, $conf) = @_;
my $role = $conf->{role};
- _subtype(
+ subtype(
$name => where => sub {
return unless defined $_ && ref($_) && $_->isa('Mouse::Object');
$_->meta->does_role($role);
next unless $COERCE{$type};
for my $coerce_type (@{ $COERCE_KEYS{$type}}) {
$_ = $value;
- next unless $SUBTYPE{$coerce_type}->();
+ next unless $TYPE{$coerce_type}->();
$_ = $value;
$_ = $COERCE{$type}->{$coerce_type}->();
return $_ if $type_constraint->();
return $value;
}
+sub enum {
+ my $name = shift;
+ my %is_valid = map { $_ => 1 } @_;
+
+ subtype(
+ $name => where => sub { $is_valid{$_} }
+ );
+}
+
1;
__END__