File Coverage

blib/lib/JSON/Schema/Modern/Vocabulary/Content.pm
Criterion Covered Total %
statement 118 122 96.7
branch 28 30 93.3
condition 5 5 100.0
subroutine 25 26 96.1
pod 0 3 0.0
total 176 186 94.6


line stmt bran cond sub pod time code
1 38     38   2037 use strict;
  38         103  
  38         1948  
2 38     38   278 use warnings;
  38         100  
  38         4202  
3             package JSON::Schema::Modern::Vocabulary::Content;
4             # vim: set ts=8 sts=2 sw=2 tw=100 et :
5             # ABSTRACT: Implementation of the JSON Schema Content vocabulary
6              
7             our $VERSION = '0.634';
8              
9 38     38   889 use 5.020;
  38         213  
10 38     38   246 use Moo;
  38         91  
  38         301  
11 38     38   17904 use strictures 2;
  38         399  
  38         1909  
12 38     38   33224 use stable 0.031 'postderef';
  38         732  
  38         332  
13 38     38   9267 use experimental 'signatures';
  38         145  
  38         218  
14 38     38   2810 no autovivification warn => qw(fetch store exists delete);
  38         106  
  38         359  
15 38     38   3969 use if "$]" >= 5.022, experimental => 're_strict';
  38         115  
  38         1227  
16 38     38   4072 no if "$]" >= 5.031009, feature => 'indirect';
  38         108  
  38         3329  
17 38     38   277 no if "$]" >= 5.033001, feature => 'multidimensional';
  38         127  
  38         2746  
18 38     38   239 no if "$]" >= 5.033006, feature => 'bareword_filehandles';
  38         90  
  38         2827  
19 38     38   259 no if "$]" >= 5.041009, feature => 'smartmatch';
  38         109  
  38         2124  
20 38     38   229 no feature 'switch';
  38         106  
  38         1610  
21 38     38   252 use Storable 'dclone';
  38         125  
  38         3451  
22 38     38   276 use Feature::Compat::Try;
  38         108  
  38         566  
23 38     38   3212 use JSON::Schema::Modern::Utilities qw(is_type A assert_keyword_type E abort);
  38         144  
  38         3613  
24 38     38   288 use namespace::clean;
  38         133  
  38         394  
25              
26             with 'JSON::Schema::Modern::Vocabulary';
27              
28 22     22 0 48 sub vocabulary ($class) {
  22         87  
  22         46  
29 22         108 'https://json-schema.org/draft/2019-09/vocab/content' => 'draft2019-09',
30             'https://json-schema.org/draft/2020-12/vocab/content' => 'draft2020-12';
31             }
32              
33 0     0 0 0 sub evaluation_order ($class) { 4 }
  0         0  
  0         0  
  0         0  
34              
35 126     126 0 6636 sub keywords ($class, $spec_version) {
  126         323  
  126         300  
  126         281  
36             return (
37 126 100       3798 $spec_version !~ /^draft[46]\z/ ? qw(contentEncoding contentMediaType) : (),
    100          
38             $spec_version !~ /^draft[467]\z/ ? 'contentSchema' : (),
39             );
40             }
41              
42 172     172   344 sub _traverse_keyword_contentEncoding ($class, $schema, $state) {
  172         317  
  172         342  
  172         356  
  172         288  
43 172         625 return assert_keyword_type($state, $schema, 'string');
44             }
45              
46 85     85   190 sub _eval_keyword_contentEncoding ($class, $data, $schema, $state) {
  85         198  
  85         197  
  85         201  
  85         165  
  85         157  
47 85 100       378 return 1 if not is_type('string', $data);
48              
49 69 100       324 if ($state->{validate_content_schemas}) {
50 19         143 my $decoder = $state->{evaluator}->get_encoding($schema->{contentEncoding});
51             abort($state, 'cannot find decoder for contentEncoding "%s"', $schema->{contentEncoding})
52 19 100       292 if not $decoder;
53              
54             # decode the data now, so we can report errors for the right keyword
55 18         43 try {
56 18         75 $state->{_content_ref} = $decoder->(\$data);
57             }
58             catch ($e) {
59 6         21 chomp $e;
60 6         39 return E($state, 'could not decode %s string: %s', $schema->{contentEncoding}, $e);
61             }
62             }
63              
64 62         379 return A($state, $schema->{$state->{keyword}});
65             }
66              
67             *_traverse_keyword_contentMediaType = \&_traverse_keyword_contentEncoding;
68              
69 85     85   175 sub _eval_keyword_contentMediaType ($class, $data, $schema, $state) {
  85         186  
  85         210  
  85         231  
  85         180  
  85         148  
70 85 100       321 return 1 if not is_type('string', $data);
71              
72 69 100       295 if ($state->{validate_content_schemas}) {
73 19         110 my $decoder = $state->{evaluator}->get_media_type($schema->{contentMediaType});
74             abort($state, 'cannot find decoder for contentMediaType "%s"', $schema->{contentMediaType})
75 19 100       64 if not $decoder;
76              
77             # contentEncoding failed to decode the content
78 18 100 100     144 return 1 if exists $schema->{contentEncoding} and not exists $state->{_content_ref};
79              
80             # decode the data now, so we can report errors for the right keyword
81 15         34 try {
82 15   100     86 $state->{_content_ref} = $decoder->($state->{_content_ref} // \$data);
83             }
84             catch ($e) {
85 6         22 chomp $e;
86 6         17 delete $state->{_content_ref};
87 6         35 return E($state, 'could not decode %s string: %s', $schema->{contentMediaType}, $e);
88             }
89             }
90              
91 59         341 return A($state, $schema->{$state->{keyword}});
92             }
93              
94 42     42   95 sub _traverse_keyword_contentSchema ($class, $schema, $state) {
  42         90  
  42         89  
  42         69  
  42         85  
95 42         273 $class->traverse_subschema($schema, $state);
96             }
97              
98 40     40   88 sub _eval_keyword_contentSchema ($class, $data, $schema, $state) {
  40         89  
  40         91  
  40         93  
  40         82  
  40         70  
99 40 50       186 return 1 if not exists $schema->{contentMediaType};
100 40 100       129 return 1 if not is_type('string', $data);
101              
102 36 100       178 if ($state->{validate_content_schemas}) {
103 6 100       28 return 1 if not exists $state->{_content_ref}; # contentMediaType failed to decode the content
104             return E($state, 'subschema is not valid')
105             if not $class->eval($state->{_content_ref}->$*, $schema->{contentSchema},
106 4 100       113 { %$state, keyword_path => $state->{keyword_path}.'/contentSchema' });
107             }
108              
109 32 50       4158 return A($state, ref $schema->{contentSchema} ? dclone($schema->{contentSchema}) : $schema->{contentSchema});
110             }
111              
112             1;
113              
114             __END__