--- /dev/null
+package Parse::Method::Signatures::TypeConstraint;
+
+use Carp qw/croak carp/;
+use Moose;
+use MooseX::Types::Util qw/has_available_type_export/;
+use MooseX::Types::Moose qw/Str HashRef CodeRef ClassName/;
+use Parse::Method::Signatures::Types qw/TypeConstraint/;
+
+use namespace::clean -except => 'meta';
+
+has ppi => (
+ is => 'ro',
+ isa => 'PPI::Element',
+ required => 1,
+ handles => {
+ 'to_string' => 'content'
+ }
+);
+
+has tc => (
+ is => 'ro',
+ isa => TypeConstraint,
+ lazy => 1,
+ builder => '_build_tc',
+);
+
+has from_namespace => (
+ is => 'ro',
+ isa => ClassName,
+ predicate => 'has_from_namespace'
+);
+
+has tc_callback => (
+ is => 'ro',
+ isa => CodeRef,
+ default => sub { \&find_registered_constraint },
+);
+
+sub find_registered_constraint {
+ my ($self, $name) = @_;
+
+ my $type;
+ if ($self->has_from_namespace) {
+ my $pkg = $self->from_namespace;
+
+ if ($type = has_available_type_export($pkg, $name)) {
+ croak "The type '$name' was found in $pkg " .
+ "but it hasn't yet been defined. Perhaps you need to move the " .
+ "definition into a type library or a BEGIN block.\n"
+ if $type && $type->isa('MooseX::Types::UndefinedType');
+ }
+ else {
+ my $meta = Class::MOP::class_of($pkg) || Class::MOP::Class->initialize($pkg);
+ my $func = $meta->get_package_symbol('&' . $name);
+ my $proto = prototype $func if $func;
+
+ $name = $func->()
+ if $func && defined $proto && !length $proto;
+ }
+ }
+
+ my $registry = Moose::Util::TypeConstraints->get_type_constraint_registry;
+ return $type || $registry->find_type_constraint($name) || $name;
+}
+
+
+sub _build_tc {
+ my ($self) = @_;
+ my $tc = $self->_walk_data($self->ppi);
+
+ # This makes the error appear from the right place
+ local $Carp::Internal{'Class::MOP::Method::Generated'} = 1
+ unless exists $Carp::Internal{'Class::MOP::Method::Generated'};
+
+ croak "'@{[$self->ppi]}' could not be parsed to a type constraint - maybe you need to "
+ . "pre-declare the type with class_type"
+ unless blessed $tc;
+ return $tc;
+}
+
+sub _walk_data {
+ my ($self, $data) = @_;
+
+ my $res = $self->_union_node($data)
+ || $self->_params_node($data)
+ || $self->_str_node($data)
+ || $self->_leaf($data)
+ or confess 'failed to visit tc';
+ return $res->();
+}
+
+sub _leaf {
+ my ($self, $data) = @_;
+
+ sub { $self->_invoke_callback($data->content) };
+}
+
+sub _union_node {
+ my ($self, $data) = @_;
+ return unless $data->isa('PPI::Statement::Expression::TCUnion');
+
+ my @types = map { $self->_walk_data($_) } $data->children;
+ sub {
+ scalar @types == 1 ? @types
+ : Moose::Meta::TypeConstraint::Union->new(type_constraints => \@types)
+ };
+}
+
+sub _params_node {
+ my ($self, $data) = @_;
+ return unless $data->isa('PPI::Statement::Expression::TCParams');
+
+ my @params = map { $self->_walk_data($_) } @{$data->params};
+ my $type = $self->_invoke_callback($data->type);
+ sub { $type->parameterize(@params) }
+}
+
+
+sub _str_node {
+ my ($self, $data) = @_;
+ return unless $data->isa('PPI::Token::StringifiedWord')
+ || $data->isa('PPI::Token::Number')
+ || $data->isa('PPI::Token::Quote');
+
+ sub {
+ $data->isa('PPI::Token::Number')
+ ? $data->content
+ : $data->string
+ };
+}
+
+sub _invoke_callback {
+ my $self = shift;
+ $self->tc_callback->($self, @_);
+}
+
+__PACKAGE__->meta->make_immutable;
+
+1;
+
+__END__
+
+=head1 NAME
+
+Parse::Method::Signatures::TypeConstraint - turn parsed TC data into Moose TC object
+
+=head1 DESCRIPTION
+
+Class used to turn PPI elements into L<Moose::Meta::TypeConstraint> objects.
+
+=head1 ATTRIBUTES
+
+=head2 tc
+
+=over
+
+B<Lazy Build.>
+
+=back
+
+The L<Moose::Meta::TypeConstraint> object for this type constraint, built when
+requested. L</tc_callback> will be called for each individual component type in
+turn.
+
+=head2 tc_callback
+
+=over
+
+B<Type:> CodeRef
+
+B<Default:> L</find_registered_constraint>
+
+=back
+
+Callback used to turn type names into type objects. See
+L<Parse::Method::Signatures/type_constraint_callback> for more details and an
+example.
+
+=head2 from_namespace
+
+=over
+
+B<Type:> ClassName
+
+=back
+
+If provided, then the default C<tc_callback> will search for L<MooseX::Types>
+in this package.
+
+=head1 METHODS
+
+=head2 find_registered_constraint
+
+Will search for an imported L<MooseX::Types> in L</from_namespace> (if
+provided). Failing that it will ask the L<Moose::Meta::TypeConstraint::Registry>
+for a type with the given name.
+
+If all else fails, it will simple return the type as a string, so that Moose's
+auto-vivification of classnames to type will work.
+
+=head2 to_string
+
+String representation of the type constraint, approximately as parsed.
+
+=head1 SEE ALSO
+
+L<Parse::Method::Signatures>, L<MooseX::Types>, L<MooseX::Types::Util>.
+
+=head1 AUTHORS
+
+Florian Ragwitz <rafl@debian.org>.
+
+Ash Berlin <ash@cpan.org>.
+
+=head1 LICENSE
+
+Licensed under the same terms as Perl itself.
+