File Coverage

blib/lib/Data/Rx/CoreType/rec.pm
Criterion Covered Total %
statement 48 48 100.0
branch 21 22 95.4
condition 11 12 91.6
subroutine 7 7 100.0
pod 0 3 0.0
total 87 92 94.5


line stmt bran cond sub pod time code
1 1     1   6 use strict;
  1         2  
  1         32  
2 1     1   5 use warnings;
  1         2  
  1         52  
3             package Data::Rx::CoreType::rec;
4             # ABSTRACT: the Rx //rec type
5             $Data::Rx::CoreType::rec::VERSION = '0.200006';
6 1     1   4 use parent 'Data::Rx::CoreType';
  1         3  
  1         5  
7              
8 1     1   121 use Scalar::Util ();
  1         2  
  1         636  
9              
10 54     54 0 194 sub subname { 'rec' }
11              
12             sub guts_from_arg {
13 15     15 0 31 my ($class, $arg, $rx, $type) = @_;
14              
15 15 100       112 Carp::croak("unknown arguments to new") unless
16             Data::Rx::Util->_x_subset_keys_y($arg, {
17             rest => 1,
18             required => 1,
19             optional => 1,
20             });
21              
22 13         36 my $guts = {};
23              
24 13         20 my $content_schema = {};
25              
26 13 100       68 $guts->{rest_schema} = $rx->make_schema($arg->{rest}) if $arg->{rest};
27              
28 13         37 TYPE: for my $type (qw(required optional)) {
29 26 100       79 next TYPE unless my $entries = $arg->{$type};
30              
31 21         68 for my $entry (keys %$entries) {
32 25 100       262 Carp::croak("$entry appears in both required and optional")
33             if $content_schema->{ $entry };
34              
35 24         88 $content_schema->{ $entry } = {
36             optional => $type eq 'optional',
37             schema => $rx->make_schema($entries->{ $entry }),
38             };
39             }
40             };
41              
42 12         27 $guts->{content_schema} = $content_schema;
43 12         38 return $guts;
44             }
45              
46             sub assert_valid {
47 406     406 0 21978 my ($self, $value) = @_;
48              
49 406 100 100     2780 unless (! Scalar::Util::blessed($value) and ref $value eq 'HASH') {
50 344         2782 $self->fail({
51             error => [ qw(type) ],
52             message => "value is not a hashref",
53             value => $value,
54             });
55             }
56              
57 62         142 my $c_schema = $self->{content_schema};
58              
59 62         84 my @subchecks;
60              
61 62         244 my @rest_keys = grep { ! exists $c_schema->{$_} } keys %$value;
  108         323  
62 62 100 100     320 if (@rest_keys and not $self->{rest_schema}) {
63 9         22 @rest_keys = sort @rest_keys;
64 9         164 push @subchecks,
65             $self->new_fail({
66             error => [ qw(unexpected) ],
67             keys => [@rest_keys],
68             message => "found unexpected entries: @rest_keys",
69             value => $value,
70             });
71             }
72              
73 62 50       279 for my $key ($self->rx->sort_keys ? sort keys %$c_schema : keys %$c_schema) {
74 99         332 my $check = $c_schema->{$key};
75              
76 99 100 66     605 if (not $check->{optional} and not exists $value->{ $key }) {
77 23         379 push @subchecks,
78             $self->new_fail({
79             error => [ qw(missing) ],
80             keys => [$key],
81             message => "no value given for required entry $key",
82             value => $value,
83             });
84 23         115 next;
85             }
86              
87 76 100       198 if (exists $value->{$key}) {
88 59 100       590 push @subchecks, [
89             $value->{$key},
90             $check->{schema},
91             { data_path => [ [$key, 'key' ] ],
92             check_path => [
93             [ $check->{optional} ? 'optional' : 'required', 'key' ],
94             [ $key, 'key' ],
95             ],
96             },
97             ];
98             }
99             }
100              
101 62 100 100     1075 if (@rest_keys && $self->{rest_schema}) {
102 21         35 my %rest = map { $_ => $value->{$_} } @rest_keys;
  40         120  
103              
104 21         103 push @subchecks, [
105             \%rest,
106             $self->{rest_schema},
107             { check_path => [ ['rest', 'key' ] ],
108             },
109             ];
110             }
111              
112 62         374 $self->perform_subchecks(\@subchecks);
113              
114 25         253 return 1;
115             }
116              
117             1;
118              
119             __END__