summaryrefslogtreecommitdiffstats
path: root/lib/MooseX/Meta/TypeConstraint/Doctype.pm
diff options
context:
space:
mode:
Diffstat (limited to 'lib/MooseX/Meta/TypeConstraint/Doctype.pm')
-rw-r--r--lib/MooseX/Meta/TypeConstraint/Doctype.pm133
1 files changed, 133 insertions, 0 deletions
diff --git a/lib/MooseX/Meta/TypeConstraint/Doctype.pm b/lib/MooseX/Meta/TypeConstraint/Doctype.pm
new file mode 100644
index 0000000..0d333cc
--- /dev/null
+++ b/lib/MooseX/Meta/TypeConstraint/Doctype.pm
@@ -0,0 +1,133 @@
+package MooseX::Meta::TypeConstraint::Doctype;
+use Moose;
+
+use Devel::PartialDump 'dump';
+use Moose::Util::TypeConstraints qw(class_type find_type_constraint
+ match_on_type);
+use Scalar::Util 'weaken';
+
+use MooseX::Validation::Doctypes::Errors;
+
+extends 'Moose::Meta::TypeConstraint';
+
+class_type('Moose::Meta::TypeConstraint');
+
+has doctype => (
+ is => 'ro',
+ isa => 'Ref',
+ required => 1,
+);
+
+has '+parent' => (
+ default => sub { find_type_constraint('Ref') },
+);
+
+has '+constraint' => (
+ lazy => 1,
+ default => sub {
+ weaken(my $self = shift);
+ return sub { !$self->validate_doctype($_) };
+ },
+);
+
+has '+message' => (
+ default => sub {
+ weaken(my $self = shift);
+ return sub { $self->validate_doctype($_) };
+ },
+);
+
+sub validate_doctype {
+ my $self = shift;
+ my ($data, $doctype, $prefix) = @_;
+
+ $doctype = $self->doctype
+ unless defined $doctype;
+ $prefix = ''
+ unless defined $prefix;
+
+ my ($errors, $extra_data);
+
+ match_on_type $doctype => (
+ 'HashRef' => sub {
+ if (!find_type_constraint('HashRef')->check($data)) {
+ $errors = $data;
+ }
+ else {
+ for my $key (keys %$doctype) {
+ my $sub_errors = $self->validate_doctype(
+ $data->{$key},
+ $doctype->{$key},
+ join('.', (length($prefix) ? $prefix : ()), $key)
+ );
+ if ($sub_errors) {
+ if ($sub_errors->has_errors) {
+ $errors ||= {};
+ $errors->{$key} = $sub_errors->errors;
+ }
+ if ($sub_errors->has_extra_data) {
+ $extra_data ||= {};
+ $extra_data->{$key} = $sub_errors->extra_data;
+ }
+ }
+ }
+ for my $key (keys %$data) {
+ if (!exists $doctype->{$key}) {
+ $extra_data ||= {};
+ $extra_data->{$key} = $data->{$key};
+ }
+ }
+ }
+ },
+ 'ArrayRef' => sub {
+ if (!find_type_constraint('ArrayRef')->check($data)) {
+ $errors = $data;
+ }
+ else {
+ for my $i (0..$#$doctype) {
+ my $sub_errors = $self->validate_doctype(
+ $data->[$i],
+ $doctype->[$i],
+ join('.', (length($prefix) ? $prefix : ()), "[$i]")
+ );
+ if ($sub_errors) {
+ if ($sub_errors->has_errors) {
+ $errors ||= [];
+ $errors->[$i] = $sub_errors->errors;
+ }
+ if ($sub_errors->has_extra_data) {
+ $extra_data ||= [];
+ $extra_data->[$i] = $sub_errors->extra_data;
+ }
+ }
+ }
+ for my $i (0..$#$data) {
+ next if $i < @$doctype;
+ $extra_data ||= [];
+ $extra_data->[$i] = $data->[$i];
+ }
+ }
+ },
+ 'Str|Moose::Meta::TypeConstraint' => sub {
+ my $tc = Moose::Util::TypeConstraints::find_or_parse_type_constraint($doctype);
+ die "Unknown type $doctype" unless $tc;
+ if (!$tc->check($data)) {
+ $errors = "invalid value " . dump($data) . " for '$prefix'";
+ }
+ },
+ => sub {
+ die "Unknown doctype at position '$prefix': " . dump($doctype);
+ },
+ );
+
+ return unless $errors || $extra_data;
+
+ return MooseX::Validation::Doctypes::Errors->new(
+ ($errors ? (errors => $errors) : ()),
+ ($extra_data ? (extra_data => $extra_data) : ()),
+ );
+}
+
+no Moose;
+
+1;