File Coverage

blib/lib/JSON/Schema/Modern/Vocabulary/Content.pm
Criterion Covered Total %
statement 121 125 96.8
branch 28 30 93.3
condition 5 5 100.0
subroutine 25 26 96.1
pod 0 3 0.0
total 179 189 94.7


line stmt bran cond sub pod time code
1 38     38   1628 use strict;
  38         76  
  38         1375  
2 38     38   179 use warnings;
  38         68  
  38         3081  
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.638';
8              
9 38     38   590 use 5.020;
  38         126  
10 38     38   161 use Moo;
  38         66  
  38         222  
11 38     38   12175 use strictures 2;
  38         283  
  38         1322  
12 38     38   14014 use stable 0.031 'postderef';
  38         621  
  38         224  
13 38     38   6317 use experimental 'signatures';
  38         84  
  38         161  
14 38     38   1890 no autovivification warn => qw(fetch store exists delete);
  38         302  
  38         233  
15 38     38   2634 use if "$]" >= 5.022, experimental => 're_strict';
  38         84  
  38         837  
16 38     38   2771 no if "$]" >= 5.031009, feature => 'indirect';
  38         95  
  38         2175  
17 38     38   214 no if "$]" >= 5.033001, feature => 'multidimensional';
  38         67  
  38         1909  
18 38     38   164 no if "$]" >= 5.033006, feature => 'bareword_filehandles';
  38         68  
  38         1850  
19 38     38   180 no if "$]" >= 5.041009, feature => 'smartmatch';
  38         85  
  38         1586  
20 38     38   213 no feature 'switch';
  38         75  
  38         1021  
21 38     38   179 use Storable 'dclone';
  38         69  
  38         2308  
22 38     38   343 use Feature::Compat::Try;
  38         78  
  38         499  
23 38     38   2405 use JSON::Schema::Modern::Utilities qw(is_type A assert_keyword_type E abort jsonp_set decode_media_type);
  38         457  
  38         3012  
24 38     38   185 use namespace::clean;
  38         228  
  38         577  
25              
26             with 'JSON::Schema::Modern::Vocabulary';
27              
28 22     22 0 43 sub vocabulary ($class) {
  22         36  
  22         35  
29 22         73 '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 1278 sub keywords ($class, $spec_version) {
  126         224  
  126         244  
  126         219  
36             return (
37 126 100       2559 $spec_version !~ /^draft[46]\z/ ? qw(contentEncoding contentMediaType) : (),
    100          
38             $spec_version !~ /^draft[467]\z/ ? 'contentSchema' : (),
39             );
40             }
41              
42 174     174   267 sub _traverse_keyword_contentEncoding ($class, $schema, $state) {
  174         249  
  174         243  
  174         246  
  174         223  
43 174         531 return assert_keyword_type($state, $schema, 'string');
44             }
45              
46 86     86   139 sub _eval_keyword_contentEncoding ($class, $data, $schema, $state) {
  86         136  
  86         162  
  86         141  
  86         141  
  86         120  
47 86 100       313 return 1 if not is_type('string', $data);
48              
49 70 100       249 if ($state->{validate_content_schemas}) {
50 20         99 my $decoder = $state->{evaluator}->get_encoding($schema->{contentEncoding});
51             abort($state, 'cannot find decoder for contentEncoding "%s"', $schema->{contentEncoding})
52 20 100       212 if not $decoder;
53              
54             # decode the data now, so we can report errors for the right keyword
55 19         31 try {
56 19         47 $state->{_content_ref} = $decoder->(\$data);
57 13         58 $state->{data} = jsonp_set($state->{data}, $state->{data_path}, $state->{_content_ref}->$*);
58             }
59             catch ($e) {
60 6         18 chomp $e;
61 6         32 return E($state, 'could not decode %s string: %s', $schema->{contentEncoding}, $e);
62             }
63             }
64              
65 63         314 return A($state, $schema->{$state->{keyword}});
66             }
67              
68             *_traverse_keyword_contentMediaType = \&_traverse_keyword_contentEncoding;
69              
70 86     86   156 sub _eval_keyword_contentMediaType ($class, $data, $schema, $state) {
  86         134  
  86         138  
  86         138  
  86         134  
  86         133  
71 86 100       226 return 1 if not is_type('string', $data);
72              
73 70 100       206 if ($state->{validate_content_schemas}) {
74             # contentEncoding failed to decode the content
75 20 100 100     87 return 1 if exists $schema->{contentEncoding} and not exists $state->{_content_ref};
76              
77             # decode the data now, so we can report errors for the right keyword
78 17         24 my $contentref;
79 17         28 try {
80 17   100     96 $contentref = decode_media_type($schema->{contentMediaType}, $state->{_content_ref} // \$data);
81             }
82             catch ($e) {
83 6         17 chomp $e;
84 6         14 delete $state->{_content_ref};
85 6         24 return E($state, 'could not decode %s string: %s', $schema->{contentMediaType}, $e);
86             }
87              
88             abort($state, 'cannot find decoder for contentMediaType "%s"', $schema->{contentMediaType})
89 11 100       31 if not $contentref;
90              
91 10         37 $state->{_content_ref} = $contentref;
92 10         40 $state->{data} = jsonp_set($state->{data}, $state->{data_path}, $state->{_content_ref}->$*);
93             }
94              
95 60         199 return A($state, $schema->{$state->{keyword}});
96             }
97              
98 43     43   72 sub _traverse_keyword_contentSchema ($class, $schema, $state) {
  43         73  
  43         73  
  43         72  
  43         69  
99 43         221 $class->traverse_subschema($schema, $state);
100             }
101              
102 41     41   68 sub _eval_keyword_contentSchema ($class, $data, $schema, $state) {
  41         78  
  41         97  
  41         73  
  41         67  
  41         60  
103 41 50       156 return 1 if not exists $schema->{contentMediaType};
104 41 100       118 return 1 if not is_type('string', $data);
105              
106 37 100       114 if ($state->{validate_content_schemas}) {
107 7 100       27 return 1 if not exists $state->{_content_ref}; # contentMediaType failed to decode the content
108             return E($state, 'subschema is not valid')
109             if not $class->eval($state->{_content_ref}->$*, $schema->{contentSchema},
110 5 100       114 { %$state, keyword_path => $state->{keyword_path}.'/contentSchema' });
111             }
112              
113 33 50       3233 return A($state, ref $schema->{contentSchema} ? dclone($schema->{contentSchema}) : $schema->{contentSchema});
114             }
115              
116             1;
117              
118             __END__