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 50     50   153255 use strict;
  50         56  
  50         1428  
2 50     50   165 use warnings;
  50         91  
  50         2344  
3             package YAML::PP::Schema;
4 50     50   215 use B;
  50         1656  
  50         1120  
5 50     50   19237 use Module::Load qw//;
  50         55093  
  50         3547  
6              
7             our $VERSION = 'v0.41.0'; # VERSION
8              
9 50     50   15526 use YAML::PP::Common qw/ YAML_PLAIN_SCALAR_STYLE /;
  50         142  
  50         3411  
10              
11 50     50   281 use Carp qw/ croak /;
  50         135  
  50         2097  
12 50     50   228 use Scalar::Util qw/ blessed /;
  50         70  
  50         112328  
13              
14             sub new {
15 770     770 0 2295 my ($class, %args) = @_;
16              
17 770         1711 my $yaml_version = delete $args{yaml_version};
18 770         1420 my $bool = delete $args{boolean};
19 770 50       1730 $bool = 'perl' unless defined $bool;
20 770 50       1691 if (keys %args) {
21 0         0 die "Unexpected arguments: " . join ', ', sort keys %args;
22             }
23 770         1691 my $true;
24             my $false;
25 770         0 my @bool_class;
26 770         2315 my @bools = split m/,/, $bool;
27 770         1340 for my $b (@bools) {
28 763 50 66     2501 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         4247 require JSON::PP;
34 595   50     2964 $true ||= \&_bool_jsonpp_true;
35 595   50     2289 $false ||= \&_bool_jsonpp_false;
36 595         1397 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 168         385 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 770   100     2128 $true ||= \&_bool_perl_true;
53 770   100     1775 $false ||= \&_bool_perl_false;
54              
55 770         6840 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 770         4482 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 770         2934 return $self;
79             }
80              
81 52812     52812 0 65137 sub resolvers { return $_[0]->{resolvers} }
82 25077     25077 0 28698 sub representers { return $_[0]->{representers} }
83              
84 2293     2293 0 4218 sub true { return $_[0]->{true} }
85 2293     2293 0 4332 sub false { return $_[0]->{false} }
86 1604 100   1604 0 1528 sub bool_class { return @{ $_[0]->{bool_class} } ? $_[0]->{bool_class} : undef }
  1604         4256  
87 788     788 0 1696 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 770     770 0 1708 my ($self, @schemas) = @_;
99 770         1850 my $yaml_version = $self->yaml_version;
100 770         1142 my $i = 0;
101 770         2021 while ($i < @schemas) {
102 1389         2238 my $item = $schemas[ $i ];
103 1389 100       2858 if ($item eq '+') {
104 457         912 $item = $DEFAULT_SCHEMA{ $yaml_version };
105             }
106 1389         1713 $i++;
107 1389 100       2330 if (blessed($item)) {
108 10         34 $item->register(
109             schema => $self,
110             );
111 10         49 next;
112             }
113 1379         1557 my @options;
114 1379   100     6105 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         24 $i++;
123             }
124              
125 1379         1783 my $class;
126 1379 100       2799 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         5 Module::Load::load $class;
132             }
133             else {
134 1378         2388 $class = "YAML::PP::Schema::$item";
135 1378 50       5327 unless ($class =~ m/\A[A-Za-z0-9_:]+\z/) {
136 0         0 die "Module name '$class' is invalid";
137             }
138 1378   66     6297 $LOADED_SCHEMA{ $item } ||= Module::Load::load $class;
139             }
140 1379         84775 $class->register(
141             schema => $self,
142             options => \@options,
143             );
144              
145             }
146             }
147              
148             sub add_resolver {
149 21820     21820 0 32095 my ($self, %args) = @_;
150 21820         21521 my $tag = $args{tag};
151 21820         21076 my $rule = $args{match};
152 21820         24605 my $resolvers = $self->resolvers;
153 21820         26853 my ($type, @rule) = @$rule;
154 21820         21119 my $implicit = $args{implicit};
155 21820 100       26997 $implicit = 1 unless defined $implicit;
156 21820         19666 my $resolver_list = [];
157 21820 50       25674 if ($tag) {
158 21820 100       23362 if (ref $tag eq 'Regexp') {
159 681   100     2074 my $res = $resolvers->{tags} ||= [];
160 681         1309 push @$res, [ $tag, {} ];
161 681         1133 push @$resolver_list, $res->[-1]->[1];
162             }
163             else {
164 21139   100     34540 my $res = $resolvers->{tag}->{ $tag } ||= {};
165 21139         24895 push @$resolver_list, $res;
166             }
167             }
168 21820 100       26004 if ($implicit) {
169 21073   100     28939 push @$resolver_list, $resolvers->{value} ||= {};
170             }
171 21820         22824 for my $res (@$resolver_list) {
172 42893 100       47668 if ($type eq 'equals') {
    100          
    50          
173 34554         36079 my ($match, $value) = @rule;
174 34554 50       45242 unless (exists $res->{equals}->{ $match }) {
175 34554         45876 $res->{equals}->{ $match } = $value;
176             }
177 34554         64357 next;
178             }
179             elsif ($type eq 'regex') {
180 6066         6573 my ($match, $value) = @rule;
181 6066         5254 push @{ $res->{regex} }, [ $match => $value ];
  6066         13323  
182             }
183             elsif ($type eq 'all') {
184 2273         2540 my ($value) = @rule;
185 2273         5233 $res->{all} = $value;
186             }
187             }
188             }
189              
190             sub add_sequence_resolver {
191 669     669 0 1412 my ($self, %args) = @_;
192 669         1740 return $self->add_collection_resolver(sequence => %args);
193             }
194              
195             sub add_mapping_resolver {
196 874     874 0 1676 my ($self, %args) = @_;
197 874         1621 return $self->add_collection_resolver(mapping => %args);
198             }
199              
200             sub add_collection_resolver {
201 1543     1543 0 2585 my ($self, $type, %args) = @_;
202 1543         1909 my $tag = $args{tag};
203 1543         1571 my $implicit = $args{implicit};
204 1543         2009 my $resolvers = $self->resolvers;
205              
206 1543 100 66     3708 if ($tag and ref $tag eq 'Regexp') {
    50          
207 1394   100     3784 my $res = $resolvers->{ $type }->{tags} ||= [];
208             push @$res, [ $tag, {
209             on_create => $args{on_create},
210             on_data => $args{on_data},
211 1394         4581 } ];
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     868 };
218             }
219             }
220              
221             sub add_representer {
222 21226     21226 0 29659 my ($self, %args) = @_;
223              
224 21226         24504 my $representers = $self->representers;
225 21226 100       27133 if (my $flags = $args{flags}) {
226 1530         1878 my $rep = $representers->{flags};
227 1530         1893 push @$rep, \%args;
228 1530         2285 return;
229             }
230 19696 100       23148 if (my $regex = $args{regex}) {
231 766         1005 my $rep = $representers->{regex};
232 766         1119 push @$rep, \%args;
233 766         1214 return;
234             }
235 18930 100       22397 if (my $regex = $args{class_matches}) {
236 26         32 my $rep = $representers->{class_matches};
237 26         47 push @$rep, [ $args{class_matches}, $args{code} ];
238 26         56 return;
239             }
240 18904 100 66     24754 if (my $bool = $args{bool} and $] >= 5.036000) {
241             $representers->{bool} = {
242             code => $args{code},
243 165         368 };
244 165         312 return;
245             }
246 18739 100       22307 if (my $class_equals = $args{class_equals}) {
247 595         782 my $rep = $representers->{class_equals};
248             $rep->{ $class_equals } = {
249             code => $args{code},
250 595         1270 };
251 595         1300 return;
252             }
253 18144 100       20704 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         4 return;
257             }
258 18142 50       21403 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 18142 100       22630 if (defined(my $equals = $args{equals})) {
266 17281         16254 my $rep = $representers->{equals};
267             $rep->{ $equals } = {
268             code => $args{code},
269 17281         26710 };
270 17281         34376 return;
271             }
272 861 100       1551 if (defined(my $scalarref = $args{scalarref})) {
273             $representers->{scalarref} = {
274             code => $args{code},
275 24         47 };
276 24         50 return;
277             }
278 837 100       1370 if (defined(my $refref = $args{refref})) {
279             $representers->{refref} = {
280             code => $args{code},
281 24         43 };
282 24         52 return;
283             }
284 813 100       1443 if (defined(my $coderef = $args{coderef})) {
285             $representers->{coderef} = {
286             code => $args{code},
287 24         64 };
288 24         44 return;
289             }
290 789 100       1347 if (defined(my $glob = $args{glob})) {
291             $representers->{glob} = {
292             code => $args{code},
293 24         37 };
294 24         46 return;
295             }
296 765 50       1453 if (my $undef = $args{undefined}) {
297 765         1072 $representers->{undef} = $undef;
298 765         1539 return;
299             }
300             }
301              
302             sub load_scalar {
303 26588     26588 0 33111 my ($self, $constructor, $event) = @_;
304 26588         38804 my $tag = $event->{tag};
305 26588         32687 my $value = $event->{value};
306              
307 26588         40018 my $resolvers = $self->resolvers;
308 26588         26499 my $res;
309 26588 100       33724 if ($tag) {
310 697 100       1227 if ($tag eq '!') {
311 7         18 return $value;
312             }
313 690         1549 $res = $resolvers->{tag}->{ $tag };
314 690 100 66     1747 if (not $res and my $matches = $resolvers->{tags}) {
315 56         97 for my $match (@$matches) {
316 76         134 my ($re, $rule) = @$match;
317 76 100       427 if ($tag =~ $re) {
318 56         193 $res = $rule;
319 56         99 last;
320             }
321             }
322             }
323             #unless ($res) {
324             # croak "Unknown tag '$tag'. Use schema 'Catchall' to ignore unknown tags";
325             #}
326             }
327             else {
328 25891         29445 $res = $resolvers->{value};
329 25891 100       43203 if ($event->{style} ne YAML_PLAIN_SCALAR_STYLE) {
330 2658         6225 return $value;
331             }
332             }
333              
334 23923 100       38226 if (my $equals = $res->{equals}) {
335 23303 100       41442 if (exists $equals->{ $value }) {
336 426         574 my $res = $equals->{ $value };
337 426 100       856 if (ref $res eq 'CODE') {
338 82         178 return $res->($constructor, $event);
339             }
340 344         730 return $res;
341             }
342             }
343 23497 100       35000 if (my $regex = $res->{regex}) {
344 22864         32212 for my $item (@$regex) {
345 61704         72896 my ($re, $sub) = @$item;
346 61704         243907 my @matches = $value =~ $re;
347 61704 100       94754 if (@matches) {
348 9875         20329 return $sub->($constructor, $event, \@matches);
349             }
350             }
351             }
352 13622 100       21291 if (my $catch_all = $res->{all}) {
353 13595 50       24261 if (ref $catch_all eq 'CODE') {
354 13595         27914 return $catch_all->($constructor, $event);
355             }
356 0         0 return $catch_all;
357             }
358 27         90 return $value;
359             }
360              
361             sub create_sequence {
362 1230     1230 0 1809 my ($self, $constructor, $event) = @_;
363 1230         1700 my $tag = $event->{tag};
364 1230         1407 my $data = [];
365 1230         1246 my $on_data;
366              
367 1230         2117 my $resolvers = $self->resolvers->{sequence};
368 1230 100       2053 if ($tag) {
369 35 100       91 if (my $equals = $resolvers->{tag}->{ $tag }) {
370 6         10 my $on_create = $equals->{on_create};
371 6         9 $on_data = $equals->{on_data};
372 6 50       22 $on_create and $data = $on_create->($constructor, $event);
373 6         20 return ($data, $on_data);
374             }
375 29 50       66 if (my $matches = $resolvers->{tags}) {
376 29         48 for my $match (@$matches) {
377 31         67 my ($re, $actions) = @$match;
378 31         42 my $on_create = $actions->{on_create};
379 31 100       197 if ($tag =~ $re) {
380 29         42 $on_data = $actions->{on_data};
381 29 100       80 $on_create and $data = $on_create->($constructor, $event);
382 29         84 return ($data, $on_data);
383             }
384             }
385             }
386             #croak "Unknown tag '$tag'. Use schema 'Catchall' to ignore unknown tags";
387             }
388              
389 1195         2533 return ($data, $on_data);
390             }
391              
392             sub create_mapping {
393 1631     1631 0 2464 my ($self, $constructor, $event) = @_;
394 1631         2278 my $tag = $event->{tag};
395 1631         1894 my $data = {};
396 1631         1697 my $on_data;
397              
398 1631         3033 my $resolvers = $self->resolvers->{mapping};
399 1631 100       2830 if ($tag) {
400 83 100       249 if (my $equals = $resolvers->{tag}->{ $tag }) {
401 24         39 my $on_create = $equals->{on_create};
402 24         38 $on_data = $equals->{on_data};
403 24 100       83 $on_create and $data = $on_create->($constructor, $event);
404 24         88 return ($data, $on_data);
405             }
406 59 50       171 if (my $matches = $resolvers->{tags}) {
407 59         124 for my $match (@$matches) {
408 146         211 my ($re, $actions) = @$match;
409 146         165 my $on_create = $actions->{on_create};
410 146 100       604 if ($tag =~ $re) {
411 59         86 $on_data = $actions->{on_data};
412 59 100       196 $on_create and $data = $on_create->($constructor, $event);
413 59         171 return ($data, $on_data);
414             }
415             }
416             }
417             #croak "Unknown tag '$tag'. Use schema 'Catchall' to ignore unknown tags";
418             }
419              
420 1548         3296 return ($data, $on_data);
421             }
422              
423 31     31   121 sub _bool_jsonpp_true { JSON::PP::true() }
424              
425 0     0   0 sub _bool_booleanpm_true { boolean::true() }
426              
427 16     16   41 sub _bool_perl_true { !!1 }
428              
429 33     33   142 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__