File: //usr/share/perl5/Types/TypeTiny.pm
package Types::TypeTiny;
use strict;
use warnings;
our $AUTHORITY = 'cpan:TOBYINK';
our $VERSION = '1.008001';
$VERSION =~ tr/_//d;
use Scalar::Util qw< blessed refaddr weaken >;
our @EXPORT_OK = (
map(@{[ $_, "is_$_", "assert_$_" ]}, __PACKAGE__->type_names),
qw/to_TypeTiny/
);
our %EXPORT_TAGS = (
types => [ __PACKAGE__->type_names ],
is => [ map "is_$_", __PACKAGE__->type_names ],
assert => [ map "assert_$_", __PACKAGE__->type_names ],
);
my %cache;
# This `import` method is designed to avoid loading Exporter::Tiny.
# This is so that if you stick to only using the purely OO parts of
# Type::Tiny, you can skip loading the exporter.
#
sub import
{
# If this sub succeeds, it will replace itself.
# uncoverable subroutine
return unless @_ > 1; # uncoverable statement
no warnings "redefine"; # uncoverable statement
our @ISA = qw( Exporter::Tiny ); # uncoverable statement
require Exporter::Tiny; # uncoverable statement
my $next = \&Exporter::Tiny::import; # uncoverable statement
*import = $next; # uncoverable statement
my $class = shift; # uncoverable statement
my $opts = { ref($_[0]) ? %{+shift} : () }; # uncoverable statement
$opts->{into} ||= scalar(caller); # uncoverable statement
return $class->$next($opts, @_); # uncoverable statement
}
for (__PACKAGE__->type_names) {
eval qq{
sub is_$_ { $_()->check(shift) }
sub assert_$_ { $_()->assert_return(shift) }
};
}
sub meta
{
return $_[0];
}
sub type_names
{
qw( CodeLike StringLike TypeTiny HashLike ArrayLike _ForeignTypeConstraint );
}
sub has_type
{
my %has = map +($_ => 1), shift->type_names;
!!$has{ $_[0] };
}
sub get_type
{
my $self = shift;
return unless $self->has_type(@_);
no strict qw(refs);
&{$_[0]}();
}
sub coercion_names
{
qw();
}
sub has_coercion
{
my %has = map +($_ => 1), shift->coercion_names;
!!$has{ $_[0] };
}
sub get_coercion
{
my $self = shift;
return unless $self->has_coercion(@_);
no strict qw(refs);
&{$_[0]}(); # uncoverable statement
}
my ($__get_linear_isa_dfs, $tried_mro);
$__get_linear_isa_dfs = sub {
if (!$tried_mro && eval { require mro }) {
$__get_linear_isa_dfs = \&mro::get_linear_isa;
goto $__get_linear_isa_dfs;
}
no strict 'refs';
my $classname = shift;
my @lin = ($classname);
my %stored;
foreach my $parent (@{"$classname\::ISA"})
{
my $plin = $__get_linear_isa_dfs->($parent);
foreach (@$plin) {
next if exists $stored{$_};
push(@lin, $_);
$stored{$_} = 1;
}
}
return \@lin;
};
sub _check_overload
{
my $package = shift;
if (ref $package) {
$package = blessed($package);
return !!0 if !defined $package;
}
my $op = shift;
my $mro = $__get_linear_isa_dfs->($package);
foreach my $p (@$mro) {
my $fqmeth = $p . q{::(} . $op;
return !!1 if defined &{$fqmeth};
}
!!0;
}
sub _get_check_overload_sub {
if ($Type::Tiny::AvoidCallbacks) {
return '(sub { require overload; overload::Overloaded(ref $_[0] or $_[0]) and overload::Method((ref $_[0] or $_[0]), $_[1]) })->'
}
return 'Types::TypeTiny::_check_overload';
}
sub StringLike ()
{
require Type::Tiny;
$cache{StringLike} ||= "Type::Tiny"->new(
name => "StringLike",
constraint => sub { defined($_ ) && !ref($_ ) or Scalar::Util::blessed($_ ) && Types::TypeTiny::_check_overload($_ , q[""]) },
inlined => sub { qq/defined($_[1]) && !ref($_[1]) or Scalar::Util::blessed($_[1]) && ${\ +_get_check_overload_sub() }($_[1], q[""])/ },
library => __PACKAGE__,
);
}
sub HashLike ()
{
require Type::Tiny;
$cache{HashLike} ||= "Type::Tiny"->new(
name => "HashLike",
constraint => sub { ref($_ ) eq q[HASH] or Scalar::Util::blessed($_ ) && Types::TypeTiny::_check_overload($_ , q[%{}]) },
inlined => sub { qq/ref($_[1]) eq q[HASH] or Scalar::Util::blessed($_[1]) && ${\ +_get_check_overload_sub() }($_[1], q[\%{}])/ },
library => __PACKAGE__,
);
}
sub ArrayLike ()
{
require Type::Tiny;
$cache{ArrayLike} ||= "Type::Tiny"->new(
name => "ArrayLike",
constraint => sub { ref($_ ) eq q[ARRAY] or Scalar::Util::blessed($_ ) && Types::TypeTiny::_check_overload($_ , q[@{}]) },
inlined => sub { qq/ref($_[1]) eq q[ARRAY] or Scalar::Util::blessed($_[1]) && ${\ +_get_check_overload_sub() }($_[1], q[\@{}])/ },
library => __PACKAGE__,
);
}
sub CodeLike ()
{
require Type::Tiny;
$cache{CodeLike} ||= "Type::Tiny"->new(
name => "CodeLike",
constraint => sub { ref($_ ) eq q[CODE] or Scalar::Util::blessed($_ ) && Types::TypeTiny::_check_overload($_ , q[&{}]) },
inlined => sub { qq/ref($_[1]) eq q[CODE] or Scalar::Util::blessed($_[1]) && ${\ +_get_check_overload_sub() }($_[1], q[\&{}])/ },
library => __PACKAGE__,
);
}
sub TypeTiny ()
{
require Type::Tiny;
$cache{TypeTiny} ||= "Type::Tiny"->new(
name => "TypeTiny",
constraint => sub { Scalar::Util::blessed($_ ) && $_ ->isa(q[Type::Tiny]) },
inlined => sub { my $var = $_[1]; "Scalar::Util::blessed($var) && $var\->isa(q[Type::Tiny])" },
library => __PACKAGE__,
_build_coercion => sub {
my $c = shift;
$c->add_type_coercions(_ForeignTypeConstraint(), \&to_TypeTiny);
$c->freeze;
},
);
}
sub _ForeignTypeConstraint ()
{
require Type::Tiny;
$cache{_ForeignTypeConstraint} ||= "Type::Tiny"->new(
name => "_ForeignTypeConstraint",
constraint => \&_is_ForeignTypeConstraint,
inlined => sub { qq/ref($_[1]) && do { require Types::TypeTiny; Types::TypeTiny::_is_ForeignTypeConstraint($_[1]) }/ },
library => __PACKAGE__,
);
}
my %ttt_cache;
sub _is_ForeignTypeConstraint
{
my $t = @_ ? $_[0] : $_;
return !!1 if ref $t eq 'CODE';
if (my $class = blessed $t)
{
return !!0 if $class->isa("Type::Tiny");
return !!1 if $class->isa("Moose::Meta::TypeConstraint");
return !!1 if $class->isa("MooseX::Types::TypeDecorator");
return !!1 if $class->isa("Validation::Class::Simple");
return !!1 if $class->isa("Validation::Class");
return !!1 if $t->can("check") && $t->can("get_message");
}
!!0;
}
sub to_TypeTiny
{
my $t = @_ ? $_[0] : $_;
return $t unless (my $ref = ref $t);
return $t if $ref =~ /^Type::Tiny\b/;
return $ttt_cache{ refaddr($t) } if $ttt_cache{ refaddr($t) };
if (my $class = blessed $t)
{
return $t if $class->isa("Type::Tiny");
return _TypeTinyFromMoose($t) if $class eq "MooseX::Types::TypeDecorator"; # needed before MooseX::Types 0.35.
return _TypeTinyFromMoose($t) if $class->isa("Moose::Meta::TypeConstraint");
return _TypeTinyFromMoose($t) if $class->isa("MooseX::Types::TypeDecorator");
return _TypeTinyFromMouse($t) if $class->isa("Mouse::Meta::TypeConstraint");
return _TypeTinyFromValidationClass($t) if $class->isa("Validation::Class::Simple");
return _TypeTinyFromValidationClass($t) if $class->isa("Validation::Class");
return _TypeTinyFromGeneric($t) if $t->can("check") && $t->can("get_message"); # i.e. Type::API::Constraint
}
return _TypeTinyFromCodeRef($t) if $ref eq q(CODE);
$t;
}
sub _TypeTinyFromMoose
{
my $t = $_[0];
if (ref $t->{"Types::TypeTiny::to_TypeTiny"})
{
return $t->{"Types::TypeTiny::to_TypeTiny"};
}
if ($t->name ne '__ANON__')
{
require Types::Standard;
my $ts = 'Types::Standard'->get_type($t->name);
return $ts if $ts->{_is_core};
}
my %opts;
$opts{display_name} = $t->name;
$opts{constraint} = $t->constraint;
$opts{parent} = to_TypeTiny($t->parent) if $t->has_parent;
$opts{inlined} = sub { shift; $t->_inline_check(@_) } if $t->can("can_be_inlined") && $t->can_be_inlined;
$opts{message} = sub { $t->get_message($_) } if $t->has_message;
$opts{moose_type} = $t;
if ($t->can('parameterize')) {
$opts{constraint_generator} = sub {
# convert args into Moose native types; not strictly necessary
my @args = map { TypeTiny->check($_) ? $_->moose_type : $_ } @_;
my $paramd = $t->parameterize(@args);
_TypeTinyFromMoose($paramd);
};
}
require Type::Tiny;
my $new = 'Type::Tiny'->new(%opts);
$ttt_cache{ refaddr($t) } = $new;
weaken($ttt_cache{ refaddr($t) });
$new->{coercion} = do {
require Type::Coercion::FromMoose;
'Type::Coercion::FromMoose'->new(
type_constraint => $new,
moose_coercion => $t->coercion,
);
} if $t->has_coercion;
return $new;
}
sub _TypeTinyFromValidationClass
{
my $t = $_[0];
require Type::Tiny;
require Types::Standard;
my %opts = (
parent => Types::Standard::HashRef(),
_validation_class => $t,
);
if ($t->VERSION >= "7.900048")
{
$opts{constraint} = sub {
$t->params->clear;
$t->params->add(%$_);
my $f = $t->filtering; $t->filtering('off');
my $r = eval { $t->validate };
$t->filtering($f || 'pre');
return $r;
};
$opts{message} = sub {
$t->params->clear;
$t->params->add(%$_);
my $f = $t->filtering; $t->filtering('off');
my $r = (eval { $t->validate } ? "OK" : $t->errors_to_string);
$t->filtering($f || 'pre');
return $r;
};
}
else # need to use hackish method
{
$opts{constraint} = sub {
$t->params->clear;
$t->params->add(%$_);
no warnings "redefine";
local *Validation::Class::Directive::Filters::execute_filtering = sub { $_[0] };
eval { $t->validate };
};
$opts{message} = sub {
$t->params->clear;
$t->params->add(%$_);
no warnings "redefine";
local *Validation::Class::Directive::Filters::execute_filtering = sub { $_[0] };
eval { $t->validate } ? "OK" : $t->errors_to_string;
};
}
require Type::Tiny;
my $new = "Type::Tiny"->new(%opts);
$new->coercion->add_type_coercions(
Types::Standard::HashRef() => sub {
my %params = %$_;
for my $k (keys %params)
{ delete $params{$_} unless $t->get_fields($k) };
$t->params->clear;
$t->params->add(%params);
eval { $t->validate };
$t->get_hash;
},
);
$ttt_cache{ refaddr($t) } = $new;
weaken($ttt_cache{ refaddr($t) });
return $new;
}
sub _TypeTinyFromGeneric
{
my $t = $_[0];
my %opts = (
constraint => sub { $t->check(@_ ? @_ : $_) },
message => sub { $t->get_message(@_ ? @_ : $_) },
);
$opts{display_name} = $t->name if $t->can("name");
$opts{coercion} = sub { $t->coerce(@_ ? @_ : $_) }
if $t->can("has_coercion") && $t->has_coercion && $t->can("coerce");
if ($t->can('can_be_inlined') && $t->can_be_inlined && $t->can('inline_check')) {
$opts{inlined} = sub { $t->inline_check($_[1]) };
}
require Type::Tiny;
my $new = "Type::Tiny"->new(%opts);
$ttt_cache{ refaddr($t) } = $new;
weaken($ttt_cache{ refaddr($t) });
return $new;
}
sub _TypeTinyFromMouse
{
my $t = $_[0];
my %opts = (
constraint => sub { $t->check(@_ ? @_ : $_) },
message => sub { $t->get_message(@_ ? @_ : $_) },
);
$opts{display_name} = $t->name if $t->can("name");
$opts{coercion} = sub { $t->coerce(@_ ? @_ : $_) }
if $t->can("has_coercion") && $t->has_coercion && $t->can("coerce");
if ($t->{'constraint_generator'}) {
$opts{constraint_generator} = sub {
# convert args into Moose native types; not strictly necessary
my @args = map { TypeTiny->check($_) ? $_->mouse_type : $_ } @_;
my $paramd = $t->parameterize(@args);
_TypeTinyFromMouse($paramd);
};
}
require Type::Tiny;
my $new = "Type::Tiny"->new(%opts);
$ttt_cache{ refaddr($t) } = $new;
weaken($ttt_cache{ refaddr($t) });
return $new;
}
my $QFS;
sub _TypeTinyFromCodeRef
{
my $t = $_[0];
my %opts = (
constraint => sub {
return !!eval { $t->($_) };
},
message => sub {
local $@;
eval { $t->($_); 1 } or do { chomp $@; return $@ if $@ };
return sprintf('%s did not pass type constraint', Type::Tiny::_dd($_));
},
);
if ($QFS ||= "Sub::Quote"->can("quoted_from_sub"))
{
my (undef, $perlstring, $captures) = @{ $QFS->($t) || [] };
if ($perlstring)
{
$perlstring = "!!eval{ $perlstring }";
$opts{inlined} = sub
{
my $var = $_[1];
Sub::Quote::inlinify(
$perlstring,
$var,
$var eq q($_) ? '' : "local \$_ = $var;",
1,
);
} if $perlstring && !$captures;
}
}
require Type::Tiny;
my $new = "Type::Tiny"->new(%opts);
$ttt_cache{ refaddr($t) } = $new;
weaken($ttt_cache{ refaddr($t) });
return $new;
}
1;
__END__
=pod
=encoding utf-8
=for stopwords arrayfication hashification
=head1 NAME
Types::TypeTiny - type constraints used internally by Type::Tiny
=head1 STATUS
This module is covered by the
L<Type-Tiny stability policy|Type::Tiny::Manual::Policies/"STABILITY">.
=head1 DESCRIPTION
Dogfooding.
This isn't a real Type::Library-based type library; that would involve
too much circularity. But it exports some type constraints which, while
designed for use within Type::Tiny, may be more generally useful.
=head2 Types
=over
=item C<< StringLike >>
Accepts strings and objects overloading stringification.
=item C<< HashLike >>
Accepts hashrefs and objects overloading hashification.
=item C<< ArrayLike >>
Accepts arrayrefs and objects overloading arrayfication.
=item C<< CodeLike >>
Accepts coderefs and objects overloading codification.
=item C<< TypeTiny >>
Accepts blessed L<Type::Tiny> objects.
=item C<< _ForeignTypeConstraint >>
Any reference which to_TypeTiny recognizes as something that can be coerced
to a Type::Tiny object.
Yeah, the underscore is included.
=back
=head2 Coercion Functions
=over
=item C<< to_TypeTiny($constraint) >>
Promotes (or "demotes" if you prefer) a Moose::Meta::TypeConstraint object
to a Type::Tiny object.
Can also handle L<Validation::Class> objects. Type constraints built from
Validation::Class objects deliberately I<ignore> field filters when they
do constraint checking (and go to great lengths to do so); using filters for
coercion only. (The behaviour of C<coerce> if we don't do that is just too
weird!)
Can also handle any object providing C<check> and C<get_message> methods.
(This includes L<Mouse::Meta::TypeConstraint> objects.) If the object also
provides C<has_coercion> and C<coerce> methods, these will be used too.
Can also handle coderefs (but not blessed coderefs or objects overloading
C<< &{} >>). Coderefs are expected to return true iff C<< $_ >> passes the
constraint. If C<< $_ >> fails the type constraint, they may either return
false, or die with a helpful error message.
=back
=head2 Methods
These are implemented so that C<< Types::TypeTiny->meta->get_type($foo) >>
works, for rough compatibility with a real L<Type::Library> type library.
=over
=item C<< meta >>
=item C<< type_names >>
=item C<< get_type($name) >>
=item C<< has_type($name) >>
=item C<< coercion_names >>
=item C<< get_coercion($name) >>
=item C<< has_coercion($name) >>
=back
=head1 BUGS
Please report any bugs to
L<http://rt.cpan.org/Dist/Display.html?Queue=Type-Tiny>.
=head1 SEE ALSO
L<Type::Tiny>.
=head1 AUTHOR
Toby Inkster E<lt>tobyink@cpan.orgE<gt>.
=head1 COPYRIGHT AND LICENCE
This software is copyright (c) 2013-2014, 2017-2019 by Toby Inkster.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
=head1 DISCLAIMER OF WARRANTIES
THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.