File Coverage

blib/lib/JSV/Context.pm
Criterion Covered Total %
statement 96 104 92.3
branch 40 50 80.0
condition 1 3 33.3
subroutine 15 15 100.0
pod 0 6 0.0
total 152 178 85.3


line stmt bran cond sub pod time code
1             package JSV::Context;
2              
3 47     47   642 use strict;
  47         53  
  47         1084  
4 47     47   134 use warnings;
  47         46  
  47         1895  
5              
6             use Class::Accessor::Lite (
7 47         326 new => 1,
8             rw => [qw/
9             keywords
10             reference
11             original_schema
12             throw_error
13             throw_immediate
14             enable_history
15             enable_format
16             formats
17             history
18             json
19             loose_type
20             /],
21             ro => [qw/
22             errors
23             current_type
24             current_keyword
25             current_pointer
26             current_instance
27             current_schema
28             current_schema_pointer
29             schema_pointer_history
30             /],
31 47     47   143 );
  47         45  
32              
33 47     47   7624 use Carp qw(croak);
  47         46  
  47         1819  
34 47     47   143 use JSON;
  47         47  
  47         180  
35 47     47   4105 use JSV::Keyword qw(:constants);
  47         45  
  47         4685  
36 47     47   14461 use JSV::Util::Type qw(detect_instance_type detect_instance_type_loose);
  47         75  
  47         2176  
37 47     47   13573 use JSV::Result;
  47         82  
  47         1139  
38 47     47   189 use Clone qw(clone);
  47         45  
  47         32939  
39              
40             sub validate {
41 1421     1421 0 1398 my ($self, $schema, $instance) = @_;
42              
43 1421 100       2181 local $self->{current_type} = !$self->loose_type ? detect_instance_type($instance)
44             : detect_instance_type_loose($instance);
45              
46 1421         1968 local $self->{cleanup_callbacks} = [];
47              
48 1421         1059 my $rv;
49 1421         1390 eval {
50 1421         1018 for my $keyword (@{ $self->keywords->{INSTANCE_TYPE_ANY()} }) {
  1421         2172  
51 11368 100       27505 next unless exists $schema->{$keyword->keyword};
52 1224         1574 $self->apply_keyword($keyword, $schema, $instance);
53             }
54              
55 1421 100       5980 if ($self->is_matched_types(qw/integer_or_string number_or_string/)) {
    100          
    100          
    100          
    100          
56 212         173 for my $keyword (
57 212         397 @{ $self->keywords->{INSTANCE_TYPE_NUMERIC()} },
58 212         710 @{ $self->keywords->{INSTANCE_TYPE_STRING()} }
59             ) {
60 1484 100       3987 next unless exists $schema->{$keyword->keyword};
61 85         119 $self->apply_keyword($keyword, $schema, $instance);
62             }
63             }
64             elsif ($self->is_matched_types(qw/integer number/)) {
65 237         188 for my $keyword (@{ $self->keywords->{INSTANCE_TYPE_NUMERIC()} }) {
  237         381  
66 711 100       2439 next unless exists $schema->{$keyword->keyword};
67 62         90 $self->apply_keyword($keyword, $schema, $instance);
68             }
69             }
70             elsif ($self->is_matched_types(qw/string/)) {
71 439         335 for my $keyword (@{ $self->keywords->{INSTANCE_TYPE_STRING()} }) {
  439         648  
72 1756 100       5447 next unless exists $schema->{$keyword->keyword};
73 108         158 $self->apply_keyword($keyword, $schema, $instance);
74             }
75             }
76             elsif ($self->current_type eq "array") {
77 157         579 for my $keyword (@{ $self->keywords->{INSTANCE_TYPE_ARRAY()} }) {
  157         224  
78 628 100       2267 next unless exists $schema->{$keyword->keyword};
79 139         197 $self->apply_keyword($keyword, $schema, $instance);
80             }
81             }
82             elsif ($self->current_type eq "object") {
83 328         2312 for my $keyword (@{ $self->keywords->{INSTANCE_TYPE_OBJECT()} }) {
  328         501  
84             ### for addtionalProperties, patternProperties keyword without properties keyword
85 1640 50       4393 next unless ( ( grep { defined $_ && exists $schema->{$_} } ($keyword->keyword, @{$keyword->additional_keywords}) ) > 0 );
  2296 100       5977  
  1640         2595  
86             # next unless exists $schema->{$_->keyword};
87 418         638 $self->apply_keyword($keyword, $schema, $instance);
88             }
89             }
90              
91 1421 50       3503 $rv = JSV::Result->new(
92             ($self->enable_history ? (history => $self->history) : ()),
93             );
94             };
95 1421 50       2209 if ( my $e = $@ ) {
96 0         0 $self->log_error(sprintf("Unexpected error: %s", $e));
97             }
98              
99 1421         1043 while (my $cb = pop @{ $self->{cleanup_callbacks} }) {
  1542         2939  
100 121         191 $cb->();
101             }
102              
103 1421 100       1001 if ( scalar @{ $self->errors } ) {
  1421         2010  
104 587 50       2491 $rv = JSV::Result->new(
105             errors => $self->errors,
106             ($self->enable_history ? (history => $self->history) : ()),
107             );
108 587 50       1272 if ( $self->throw_error ) {
109 0         0 croak $rv;
110             }
111             }
112              
113 1421         14117 return $rv;
114             }
115              
116             sub apply_keyword {
117 2036     2036 0 2325 my ($self, $keyword, $schema, $instance) = @_;
118              
119 2036         3317 local $self->{current_keyword} = $keyword->keyword;
120 2036         1923 local $self->{current_schema} = $schema;
121 2036         1876 local $self->{current_instance} = $instance;
122              
123 2036         3872 $keyword->validate($self, $schema, $instance);
124              
125 2036 50 33     7005 if ( $ENV{JSV_DEBUG} || $self->enable_history ) {
126 0         0 push @{ $self->history }, +{
  0         0  
127             keyword => $self->current_keyword,
128             pointer => $self->current_pointer,
129             schema => $self->current_schema,
130             instance => $self->resolve_current_instance,
131             };
132             }
133             }
134              
135             sub log_error {
136 422     422 0 427 my ($self, $message) = @_;
137              
138 422         724 my $error = +{
139             keyword => $self->current_keyword,
140             pointer => $self->current_pointer,
141             schema => $self->current_schema,
142             instance => $self->resolve_current_instance,
143             schema_pointer => $self->current_schema_pointer,
144             schema_pointer_history => clone($self->schema_pointer_history),
145             message => $message,
146             };
147              
148 422 50       4922 if ( $ENV{JSV_DEBUG} ) {
149 0         0 require Data::Dump;
150 0         0 warn "history = " . Data::Dump::dump($self->history);
151 0         0 warn "error = " . Data::Dump::dump($error);
152             }
153              
154 422 50       695 if ( $self->throw_immediate ) {
155 0 0       0 croak JSV::Result->new(
156             error => $error,
157             ($self->enable_history ? (history => $self->history) : ()),
158             );
159             }
160             else {
161 422         1136 push @{ $self->{errors} }, $error;
  422         991  
162             }
163             }
164              
165             sub resolve_current_instance {
166 422     422 0 3117 my $self = shift;
167              
168 422         317 my $instance;
169 422 100       636 if ( JSON::is_bool($self->current_instance) ) {
170 11 100       69 if ( $self->current_instance == JSON::true ) {
171 9         83 $instance = "true";
172             }
173             else {
174 2         21 $instance = "false";
175             }
176             }
177             else {
178 411         2924 $instance = $self->current_instance;
179             }
180              
181 422         1796 return $instance;
182             }
183              
184             sub is_matched_types {
185 3602     3602 0 3891 my ($self, @types) = @_;
186              
187 3602 100       2972 return (grep { $self->{current_type} eq $_ } @types) > 0 ? 1 : 0;
  6232         12859  
188             }
189              
190             sub register_cleanup_callback {
191 121     121 0 99 my ($self, $cb) = @_;
192              
193 121         82 push @{ $self->{cleanup_callbacks} }, $cb;
  121         208  
194             }
195              
196             1;