File Coverage

blib/lib/YAML/PP/Schema.pm
Criterion Covered Total %
statement 235 251 93.6
branch 101 118 85.5
condition 31 43 72.0
subroutine 27 29 93.1
pod 0 16 0.0
total 394 457 86.2


line stmt bran cond sub pod time code
1 44     44   151919 use strict;
  44         62  
  44         1302  
2 44     44   185 use warnings;
  44         67  
  44         2006  
3             package YAML::PP::Schema;
4 44     44   210 use B;
  44         56  
  44         1080  
5 44     44   16748 use Module::Load qw//;
  44         48588  
  44         1622  
6              
7             our $VERSION = 'v0.39.0'; # VERSION
8              
9 44     44   11773 use YAML::PP::Common qw/ YAML_PLAIN_SCALAR_STYLE /;
  44         109  
  44         2901  
10              
11 44     44   275 use Carp qw/ croak /;
  44         129  
  44         1978  
12 44     44   226 use Scalar::Util qw/ blessed /;
  44         60  
  44         97679  
13              
14             sub new {
15 760     760 0 2213 my ($class, %args) = @_;
16              
17 760         1376 my $yaml_version = delete $args{yaml_version};
18 760         1264 my $bool = delete $args{boolean};
19 760 50       1427 $bool = 'perl' unless defined $bool;
20 760 50       1528 if (keys %args) {
21 0         0 die "Unexpected arguments: " . join ', ', sort keys %args;
22             }
23 760         1645 my $true;
24             my $false;
25 760         0 my @bool_class;
26 760         2176 my @bools = split m/,/, $bool;
27 760         1224 for my $b (@bools) {
28 753 50 66     2394 if ($b eq '*') {
    100          
    50          
    50          
29 0         0 push @bool_class, ('boolean', 'JSON::PP::Boolean');
30 0         0 last;
31             }
32             elsif ($b eq 'JSON::PP') {
33 595         4721 require JSON::PP;
34 595   50     2382 $true ||= \&_bool_jsonpp_true;
35 595   50     1879 $false ||= \&_bool_jsonpp_false;
36 595         1257 push @bool_class, 'JSON::PP::Boolean';
37             }
38             elsif ($b eq 'boolean') {
39 0         0 require boolean;
40 0   0     0 $true ||= \&_bool_booleanpm_true;
41 0   0     0 $false ||= \&_bool_booleanpm_false;
42 0         0 push @bool_class, 'boolean';
43             }
44             elsif ($b eq 'perl' or $b eq 'perl_experimental') {
45 158         352 push @bool_class, 'perl';
46             }
47             else {
48 0         0 die "Invalid value for 'boolean': '$b'. Allowed: ('perl', 'boolean', 'JSON::PP')";
49             }
50             }
51             # Ensure booleans are resolved
52 760   100     1879 $true ||= \&_bool_perl_true;
53 760   100     1695 $false ||= \&_bool_perl_false;
54              
55 760         6681 my %representers = (
56             'undef' => undef,
57             flags => [],
58             equals => {},
59             regex => [],
60             class_equals => {},
61             class_matches => [],
62             class_isa => [],
63             scalarref => undef,
64             refref => undef,
65             coderef => undef,
66             glob => undef,
67             tied_equals => {},
68             bool => undef,
69             );
70 760         4321 my $self = bless {
71             yaml_version => $yaml_version,
72             resolvers => {},
73             representers => \%representers,
74             true => $true,
75             false => $false,
76             bool_class => \@bool_class,
77             }, $class;
78 760         2525 return $self;
79             }
80              
81 33270     33270 0 42572 sub resolvers { return $_[0]->{resolvers} }
82 24797     24797 0 29579 sub representers { return $_[0]->{representers} }
83              
84 2263     2263 0 4251 sub true { return $_[0]->{true} }
85 2263     2263 0 4151 sub false { return $_[0]->{false} }
86 1584 100   1584 0 1649 sub bool_class { return @{ $_[0]->{bool_class} } ? $_[0]->{bool_class} : undef }
  1584         4647  
87 778     778 0 1612 sub yaml_version { return $_[0]->{yaml_version} }
88              
89             my %LOADED_SCHEMA = (
90             JSON => 1,
91             );
92             my %DEFAULT_SCHEMA = (
93             '1.2' => 'Core',
94             '1.1' => 'YAML1_1',
95             );
96              
97             sub load_subschemas {
98 760     760 0 1870 my ($self, @schemas) = @_;
99 760         1595 my $yaml_version = $self->yaml_version;
100 760         1229 my $i = 0;
101 760         1687 while ($i < @schemas) {
102 1379         2078 my $item = $schemas[ $i ];
103 1379 100       2506 if ($item eq '+') {
104 447         986 $item = $DEFAULT_SCHEMA{ $yaml_version };
105             }
106 1379         1982 $i++;
107 1379 100       2651 if (blessed($item)) {
108 10         41 $item->register(
109             schema => $self,
110             );
111 10         41 next;
112             }
113 1369         1508 my @options;
114 1369   100     6227 while ($i < @schemas
      100        
115             and (
116             $schemas[ $i ] =~ m/^[^A-Za-z]/
117             or
118             $schemas[ $i ] =~ m/^[a-zA-Z0-9]+=/
119             )
120             ) {
121 14         26 push @options, $schemas[ $i ];
122 14         59 $i++;
123             }
124              
125 1369         1921 my $class;
126 1369 100       2643 if ($item =~ m/^\:(.*)/) {
127 1         2 $class = "$1";
128 1 50       4 unless ($class =~ m/\A[A-Za-z0-9_:]+\z/) {
129 0         0 die "Module name '$class' is invalid";
130             }
131 1         4 Module::Load::load $class;
132             }
133             else {
134 1368         2377 $class = "YAML::PP::Schema::$item";
135 1368 50       5348 unless ($class =~ m/\A[A-Za-z0-9_:]+\z/) {
136 0         0 die "Module name '$class' is invalid";
137             }
138 1368   66     5818 $LOADED_SCHEMA{ $item } ||= Module::Load::load $class;
139             }
140 1369         84266 $class->register(
141             schema => $self,
142             options => \@options,
143             );
144              
145             }
146             }
147              
148             sub add_resolver {
149 21540     21540 0 32569 my ($self, %args) = @_;
150 21540         22375 my $tag = $args{tag};
151 21540         21599 my $rule = $args{match};
152 21540         25483 my $resolvers = $self->resolvers;
153 21540         27946 my ($type, @rule) = @$rule;
154 21540         22120 my $implicit = $args{implicit};
155 21540 100       29121 $implicit = 1 unless defined $implicit;
156 21540         21004 my $resolver_list = [];
157 21540 50       26195 if ($tag) {
158 21540 100       24784 if (ref $tag eq 'Regexp') {
159 681   100     2223 my $res = $resolvers->{tags} ||= [];
160 681         1500 push @$res, [ $tag, {} ];
161 681         1158 push @$resolver_list, $res->[-1]->[1];
162             }
163             else {
164 20859   100     35302 my $res = $resolvers->{tag}->{ $tag } ||= {};
165 20859         25796 push @$resolver_list, $res;
166             }
167             }
168 21540 100       26623 if ($implicit) {
169 20793   100     29834 push @$resolver_list, $resolvers->{value} ||= {};
170             }
171 21540         23976 for my $res (@$resolver_list) {
172 42333 100       50421 if ($type eq 'equals') {
    100          
    50          
173 34094         36909 my ($match, $value) = @rule;
174 34094 50       45807 unless (exists $res->{equals}->{ $match }) {
175 34094         46610 $res->{equals}->{ $match } = $value;
176             }
177 34094         67796 next;
178             }
179             elsif ($type eq 'regex') {
180 5986         6325 my ($match, $value) = @rule;
181 5986         5355 push @{ $res->{regex} }, [ $match => $value ];
  5986         14207  
182             }
183             elsif ($type eq 'all') {
184 2253         2616 my ($value) = @rule;
185 2253         5785 $res->{all} = $value;
186             }
187             }
188             }
189              
190             sub add_sequence_resolver {
191 669     669 0 1554 my ($self, %args) = @_;
192 669         1809 return $self->add_collection_resolver(sequence => %args);
193             }
194              
195             sub add_mapping_resolver {
196 874     874 0 1871 my ($self, %args) = @_;
197 874         1776 return $self->add_collection_resolver(mapping => %args);
198             }
199              
200             sub add_collection_resolver {
201 1543     1543 0 2934 my ($self, $type, %args) = @_;
202 1543         1939 my $tag = $args{tag};
203 1543         1728 my $implicit = $args{implicit};
204 1543         2178 my $resolvers = $self->resolvers;
205              
206 1543 100 66     4201 if ($tag and ref $tag eq 'Regexp') {
    50          
207 1394   100     4022 my $res = $resolvers->{ $type }->{tags} ||= [];
208             push @$res, [ $tag, {
209             on_create => $args{on_create},
210             on_data => $args{on_data},
211 1394         5199 } ];
212             }
213             elsif ($tag) {
214             my $res = $resolvers->{ $type }->{tag}->{ $tag } ||= {
215             on_create => $args{on_create},
216             on_data => $args{on_data},
217 149   50     928 };
218             }
219             }
220              
221             sub add_representer {
222 20946     20946 0 31019 my ($self, %args) = @_;
223              
224 20946         23690 my $representers = $self->representers;
225 20946 100       29103 if (my $flags = $args{flags}) {
226 1510         1777 my $rep = $representers->{flags};
227 1510         2210 push @$rep, \%args;
228 1510         2468 return;
229             }
230 19436 100       24739 if (my $regex = $args{regex}) {
231 756         930 my $rep = $representers->{regex};
232 756         1256 push @$rep, \%args;
233 756         1308 return;
234             }
235 18680 100       23859 if (my $regex = $args{class_matches}) {
236 26         31 my $rep = $representers->{class_matches};
237 26         53 push @$rep, [ $args{class_matches}, $args{code} ];
238 26         48 return;
239             }
240 18654 100 66     25646 if (my $bool = $args{bool} and $] >= 5.036000) {
241             $representers->{bool} = {
242             code => $args{code},
243 155         318 };
244 155         313 return;
245             }
246 18499 100       23617 if (my $class_equals = $args{class_equals}) {
247 595         809 my $rep = $representers->{class_equals};
248             $rep->{ $class_equals } = {
249             code => $args{code},
250 595         1556 };
251 595         1351 return;
252             }
253 17904 100       22639 if (my $class_isa = $args{class_isa}) {
254 2         3 my $rep = $representers->{class_isa};
255 2         3 push @$rep, [ $args{class_isa}, $args{code} ];
256 2         5 return;
257             }
258 17902 50       22666 if (my $tied_equals = $args{tied_equals}) {
259 0         0 my $rep = $representers->{tied_equals};
260             $rep->{ $tied_equals } = {
261             code => $args{code},
262 0         0 };
263 0         0 return;
264             }
265 17902 100       24060 if (defined(my $equals = $args{equals})) {
266 17051         16791 my $rep = $representers->{equals};
267             $rep->{ $equals } = {
268             code => $args{code},
269 17051         27261 };
270 17051         36424 return;
271             }
272 851 100       1798 if (defined(my $scalarref = $args{scalarref})) {
273             $representers->{scalarref} = {
274             code => $args{code},
275 24         63 };
276 24         72 return;
277             }
278 827 100       1622 if (defined(my $refref = $args{refref})) {
279             $representers->{refref} = {
280             code => $args{code},
281 24         48 };
282 24         43 return;
283             }
284 803 100       1582 if (defined(my $coderef = $args{coderef})) {
285             $representers->{coderef} = {
286             code => $args{code},
287 24         69 };
288 24         41 return;
289             }
290 779 100       1536 if (defined(my $glob = $args{glob})) {
291             $representers->{glob} = {
292             code => $args{code},
293 24         58 };
294 24         51 return;
295             }
296 755 50       1404 if (my $undef = $args{undefined}) {
297 755         1216 $representers->{undef} = $undef;
298 755         1583 return;
299             }
300             }
301              
302             sub load_scalar {
303 7661     7661 0 10151 my ($self, $constructor, $event) = @_;
304 7661         10277 my $tag = $event->{tag};
305 7661         9513 my $value = $event->{value};
306              
307 7661         11718 my $resolvers = $self->resolvers;
308 7661         8473 my $res;
309 7661 100       18614 if ($tag) {
310 697 100       1214 if ($tag eq '!') {
311 7         24 return $value;
312             }
313 690         1486 $res = $resolvers->{tag}->{ $tag };
314 690 100 66     1469 if (not $res and my $matches = $resolvers->{tags}) {
315 56         104 for my $match (@$matches) {
316 76         135 my ($re, $rule) = @$match;
317 76 100       470 if ($tag =~ $re) {
318 56         83 $res = $rule;
319 56         101 last;
320             }
321             }
322             }
323             #unless ($res) {
324             # croak "Unknown tag '$tag'. Use schema 'Catchall' to ignore unknown tags";
325             #}
326             }
327             else {
328 6964         8279 $res = $resolvers->{value};
329 6964 100       12784 if ($event->{style} ne YAML_PLAIN_SCALAR_STYLE) {
330 2622         6359 return $value;
331             }
332             }
333              
334 5032 100       9314 if (my $equals = $res->{equals}) {
335 4412 100       8715 if (exists $equals->{ $value }) {
336 426         616 my $res = $equals->{ $value };
337 426 100       829 if (ref $res eq 'CODE') {
338 82         163 return $res->($constructor, $event);
339             }
340 344         701 return $res;
341             }
342             }
343 4606 100       7980 if (my $regex = $res->{regex}) {
344 3973         6379 for my $item (@$regex) {
345 14085         18271 my ($re, $sub) = @$item;
346 14085         56436 my @matches = $value =~ $re;
347 14085 100       22796 if (@matches) {
348 560         1718 return $sub->($constructor, $event, \@matches);
349             }
350             }
351             }
352 4046 100       7223 if (my $catch_all = $res->{all}) {
353 4019 50       8204 if (ref $catch_all eq 'CODE') {
354 4019         9472 return $catch_all->($constructor, $event);
355             }
356 0         0 return $catch_all;
357             }
358 27         72 return $value;
359             }
360              
361             sub create_sequence {
362 1213     1213 0 1940 my ($self, $constructor, $event) = @_;
363 1213         1761 my $tag = $event->{tag};
364 1213         1464 my $data = [];
365 1213         1271 my $on_data;
366              
367 1213         2168 my $resolvers = $self->resolvers->{sequence};
368 1213 100       2113 if ($tag) {
369 35 100       98 if (my $equals = $resolvers->{tag}->{ $tag }) {
370 6         12 my $on_create = $equals->{on_create};
371 6         12 $on_data = $equals->{on_data};
372 6 50       61 $on_create and $data = $on_create->($constructor, $event);
373 6         23 return ($data, $on_data);
374             }
375 29 50       76 if (my $matches = $resolvers->{tags}) {
376 29         50 for my $match (@$matches) {
377 31         58 my ($re, $actions) = @$match;
378 31         46 my $on_create = $actions->{on_create};
379 31 100       212 if ($tag =~ $re) {
380 29         48 $on_data = $actions->{on_data};
381 29 100       98 $on_create and $data = $on_create->($constructor, $event);
382 29         95 return ($data, $on_data);
383             }
384             }
385             }
386             #croak "Unknown tag '$tag'. Use schema 'Catchall' to ignore unknown tags";
387             }
388              
389 1178         2586 return ($data, $on_data);
390             }
391              
392             sub create_mapping {
393 1313     1313 0 2061 my ($self, $constructor, $event) = @_;
394 1313         2007 my $tag = $event->{tag};
395 1313         1630 my $data = {};
396 1313         1447 my $on_data;
397              
398 1313         2522 my $resolvers = $self->resolvers->{mapping};
399 1313 100       2384 if ($tag) {
400 83 100       283 if (my $equals = $resolvers->{tag}->{ $tag }) {
401 24         45 my $on_create = $equals->{on_create};
402 24         33 $on_data = $equals->{on_data};
403 24 100       117 $on_create and $data = $on_create->($constructor, $event);
404 24         71 return ($data, $on_data);
405             }
406 59 50       169 if (my $matches = $resolvers->{tags}) {
407 59         109 for my $match (@$matches) {
408 146         239 my ($re, $actions) = @$match;
409 146         195 my $on_create = $actions->{on_create};
410 146 100       649 if ($tag =~ $re) {
411 59         101 $on_data = $actions->{on_data};
412 59 100       176 $on_create and $data = $on_create->($constructor, $event);
413 59         217 return ($data, $on_data);
414             }
415             }
416             }
417             #croak "Unknown tag '$tag'. Use schema 'Catchall' to ignore unknown tags";
418             }
419              
420 1230         3040 return ($data, $on_data);
421             }
422              
423 31     31   93 sub _bool_jsonpp_true { JSON::PP::true() }
424              
425 0     0   0 sub _bool_booleanpm_true { boolean::true() }
426              
427 16     16   34 sub _bool_perl_true { !!1 }
428              
429 33     33   109 sub _bool_jsonpp_false { JSON::PP::false() }
430              
431 0     0   0 sub _bool_booleanpm_false { boolean::false() }
432              
433 2     2   4 sub _bool_perl_false { !!0 }
434              
435             1;
436              
437             __END__
438              
439             =pod
440              
441             =encoding utf-8
442              
443             =head1 NAME
444              
445             YAML::PP::Schema - Schema for YAML::PP
446              
447