← Index
NYTProf Performance Profile   « line view »
For test.pl
  Run on Mon Apr 5 14:31:27 2021
Reported on Mon Apr 5 14:31:40 2021

Filename/home/leont/perl5/perlbrew/perls/perl-5.32.0/lib/5.32.0/CPAN/Meta/Validator.pm
StatementsExecuted 153 statements in 2.41ms
Subroutines
Calls P F Exclusive
Time
Inclusive
Time
Subroutine
22132µs58µsCPAN::Meta::Validator::::check_mapCPAN::Meta::Validator::check_map (recurses: max depth 1, inclusive time 13µs)
5419µs9µsCPAN::Meta::Validator::::CORE:matchCPAN::Meta::Validator::CORE:match (opcode)
1116µs6µsCPAN::Meta::Converter::::BEGIN@1CPAN::Meta::Converter::BEGIN@1
1115µs64µsCPAN::Meta::Validator::::is_validCPAN::Meta::Validator::is_valid
2114µs10µsCPAN::Meta::Validator::::versionCPAN::Meta::Validator::version
2114µs6µsCPAN::Meta::Validator::::check_listCPAN::Meta::Validator::check_list
1113µs4µsCPAN::Meta::Converter::::BEGIN@2CPAN::Meta::Converter::BEGIN@2
1113µs4µsCPAN::Meta::Validator::::release_statusCPAN::Meta::Validator::release_status
1112µs10µsCPAN::Meta::Converter::::BEGIN@3CPAN::Meta::Converter::BEGIN@3
3112µs2µsCPAN::Meta::Validator::::stringCPAN::Meta::Validator::string
1112µs2µsCPAN::Meta::Validator::::licenseCPAN::Meta::Validator::license
1112µs4µsCPAN::Meta::Validator::::booleanCPAN::Meta::Validator::boolean
1112µs2µsCPAN::Meta::Validator::::newCPAN::Meta::Validator::new
111800ns800nsCPAN::Meta::Validator::::errorsCPAN::Meta::Validator::errors
0000s0sCPAN::Meta::Validator::::_errorCPAN::Meta::Validator::_error
0000s0sCPAN::Meta::Validator::::_uri_splitCPAN::Meta::Validator::_uri_split
0000s0sCPAN::Meta::Validator::::anythingCPAN::Meta::Validator::anything
0000s0sCPAN::Meta::Validator::::custom_1CPAN::Meta::Validator::custom_1
0000s0sCPAN::Meta::Validator::::custom_2CPAN::Meta::Validator::custom_2
0000s0sCPAN::Meta::Validator::::exversionCPAN::Meta::Validator::exversion
0000s0sCPAN::Meta::Validator::::fileCPAN::Meta::Validator::file
0000s0sCPAN::Meta::Validator::::headerCPAN::Meta::Validator::header
0000s0sCPAN::Meta::Validator::::identifierCPAN::Meta::Validator::identifier
0000s0sCPAN::Meta::Validator::::moduleCPAN::Meta::Validator::module
0000s0sCPAN::Meta::Validator::::phaseCPAN::Meta::Validator::phase
0000s0sCPAN::Meta::Validator::::relationCPAN::Meta::Validator::relation
0000s0sCPAN::Meta::Validator::::string_or_undefCPAN::Meta::Validator::string_or_undef
0000s0sCPAN::Meta::Validator::::urlCPAN::Meta::Validator::url
0000s0sCPAN::Meta::Validator::::urlspecCPAN::Meta::Validator::urlspec
Call graph for these subroutines as a Graphviz dot language file.
Line State
ments
Time
on line
Calls Time
in subs
Code
1220µs16µs
# spent 6µs within CPAN::Meta::Converter::BEGIN@1 which was called: # once (6µs+0s) by CPAN::Meta::Converter::BEGIN@26 at line 1
use 5.006;
# spent 6µs making 1 call to CPAN::Meta::Converter::BEGIN@1
229µs24µs
# spent 4µs (3+800ns) within CPAN::Meta::Converter::BEGIN@2 which was called: # once (3µs+800ns) by CPAN::Meta::Converter::BEGIN@26 at line 2
use strict;
# spent 4µs making 1 call to CPAN::Meta::Converter::BEGIN@2 # spent 800ns making 1 call to strict::import
322.18ms218µs
# spent 10µs (2+8) within CPAN::Meta::Converter::BEGIN@3 which was called: # once (2µs+8µs) by CPAN::Meta::Converter::BEGIN@26 at line 3
use warnings;
# spent 10µs making 1 call to CPAN::Meta::Converter::BEGIN@3 # spent 8µs making 1 call to warnings::import
4package CPAN::Meta::Validator;
5
61300nsour $VERSION = '2.150010';
7
8#pod =head1 SYNOPSIS
9#pod
10#pod my $struct = decode_json_file('META.json');
11#pod
12#pod my $cmv = CPAN::Meta::Validator->new( $struct );
13#pod
14#pod unless ( $cmv->is_valid ) {
15#pod my $msg = "Invalid META structure. Errors found:\n";
16#pod $msg .= join( "\n", $cmv->errors );
17#pod die $msg;
18#pod }
19#pod
20#pod =head1 DESCRIPTION
21#pod
22#pod This module validates a CPAN Meta structure against the version of the
23#pod the specification claimed in the C<meta-spec> field of the structure.
24#pod
25#pod =cut
26
27#--------------------------------------------------------------------------#
28# This code copied and adapted from Test::CPAN::Meta
29# by Barbie, <barbie@cpan.org> for Miss Barbell Productions,
30# L<http://www.missbarbell.co.uk>
31#--------------------------------------------------------------------------#
32
33#--------------------------------------------------------------------------#
34# Specification Definitions
35#--------------------------------------------------------------------------#
36
3711µsmy %known_specs = (
38 '1.4' => 'http://module-build.sourceforge.net/META-spec-v1.4.html',
39 '1.3' => 'http://module-build.sourceforge.net/META-spec-v1.3.html',
40 '1.2' => 'http://module-build.sourceforge.net/META-spec-v1.2.html',
41 '1.1' => 'http://module-build.sourceforge.net/META-spec-v1.1.html',
42 '1.0' => 'http://module-build.sourceforge.net/META-spec-v1.0.html'
43);
4413µsmy %known_urls = map {$known_specs{$_} => $_} keys %known_specs;
45
4611µsmy $module_map1 = { 'map' => { ':key' => { name => \&module, value => \&exversion } } };
47
481500nsmy $module_map2 = { 'map' => { ':key' => { name => \&module, value => \&version } } };
49
5012µsmy $no_index_2 = {
51 'map' => { file => { list => { value => \&string } },
52 directory => { list => { value => \&string } },
53 'package' => { list => { value => \&string } },
54 namespace => { list => { value => \&string } },
55 ':key' => { name => \&custom_2, value => \&anything },
56 }
57};
58
5912µsmy $no_index_1_3 = {
60 'map' => { file => { list => { value => \&string } },
61 directory => { list => { value => \&string } },
62 'package' => { list => { value => \&string } },
63 namespace => { list => { value => \&string } },
64 ':key' => { name => \&string, value => \&anything },
65 }
66};
67
6812µsmy $no_index_1_2 = {
69 'map' => { file => { list => { value => \&string } },
70 dir => { list => { value => \&string } },
71 'package' => { list => { value => \&string } },
72 namespace => { list => { value => \&string } },
73 ':key' => { name => \&string, value => \&anything },
74 }
75};
76
771700nsmy $no_index_1_1 = {
78 'map' => { ':key' => { name => \&string, list => { value => \&string } },
79 }
80};
81
8211µsmy $prereq_map = {
83 map => {
84 ':key' => {
85 name => \&phase,
86 'map' => {
87 ':key' => {
88 name => \&relation,
89 %$module_map1,
90 },
91 },
92 }
93 },
94};
95
96157µsmy %definitions = (
97 '2' => {
98 # REQUIRED
99 'abstract' => { mandatory => 1, value => \&string },
100 'author' => { mandatory => 1, list => { value => \&string } },
101 'dynamic_config' => { mandatory => 1, value => \&boolean },
102 'generated_by' => { mandatory => 1, value => \&string },
103 'license' => { mandatory => 1, list => { value => \&license } },
104 'meta-spec' => {
105 mandatory => 1,
106 'map' => {
107 version => { mandatory => 1, value => \&version},
108 url => { value => \&url },
109 ':key' => { name => \&custom_2, value => \&anything },
110 }
111 },
112 'name' => { mandatory => 1, value => \&string },
113 'release_status' => { mandatory => 1, value => \&release_status },
114 'version' => { mandatory => 1, value => \&version },
115
116 # OPTIONAL
117 'description' => { value => \&string },
118 'keywords' => { list => { value => \&string } },
119 'no_index' => $no_index_2,
120 'optional_features' => {
121 'map' => {
122 ':key' => {
123 name => \&string,
124 'map' => {
125 description => { value => \&string },
126 prereqs => $prereq_map,
127 ':key' => { name => \&custom_2, value => \&anything },
128 }
129 }
130 }
131 },
132 'prereqs' => $prereq_map,
133 'provides' => {
134 'map' => {
135 ':key' => {
136 name => \&module,
137 'map' => {
138 file => { mandatory => 1, value => \&file },
139 version => { value => \&version },
140 ':key' => { name => \&custom_2, value => \&anything },
141 }
142 }
143 }
144 },
145 'resources' => {
146 'map' => {
147 license => { list => { value => \&url } },
148 homepage => { value => \&url },
149 bugtracker => {
150 'map' => {
151 web => { value => \&url },
152 mailto => { value => \&string},
153 ':key' => { name => \&custom_2, value => \&anything },
154 }
155 },
156 repository => {
157 'map' => {
158 web => { value => \&url },
159 url => { value => \&url },
160 type => { value => \&string },
161 ':key' => { name => \&custom_2, value => \&anything },
162 }
163 },
164 ':key' => { value => \&string, name => \&custom_2 },
165 }
166 },
167
168 # CUSTOM -- additional user defined key/value pairs
169 # note we can only validate the key name, as the structure is user defined
170 ':key' => { name => \&custom_2, value => \&anything },
171 },
172
173'1.4' => {
174 'meta-spec' => {
175 mandatory => 1,
176 'map' => {
177 version => { mandatory => 1, value => \&version},
178 url => { mandatory => 1, value => \&urlspec },
179 ':key' => { name => \&string, value => \&anything },
180 },
181 },
182
183 'name' => { mandatory => 1, value => \&string },
184 'version' => { mandatory => 1, value => \&version },
185 'abstract' => { mandatory => 1, value => \&string },
186 'author' => { mandatory => 1, list => { value => \&string } },
187 'license' => { mandatory => 1, value => \&license },
188 'generated_by' => { mandatory => 1, value => \&string },
189
190 'distribution_type' => { value => \&string },
191 'dynamic_config' => { value => \&boolean },
192
193 'requires' => $module_map1,
194 'recommends' => $module_map1,
195 'build_requires' => $module_map1,
196 'configure_requires' => $module_map1,
197 'conflicts' => $module_map2,
198
199 'optional_features' => {
200 'map' => {
201 ':key' => { name => \&string,
202 'map' => { description => { value => \&string },
203 requires => $module_map1,
204 recommends => $module_map1,
205 build_requires => $module_map1,
206 conflicts => $module_map2,
207 ':key' => { name => \&string, value => \&anything },
208 }
209 }
210 }
211 },
212
213 'provides' => {
214 'map' => {
215 ':key' => { name => \&module,
216 'map' => {
217 file => { mandatory => 1, value => \&file },
218 version => { value => \&version },
219 ':key' => { name => \&string, value => \&anything },
220 }
221 }
222 }
223 },
224
225 'no_index' => $no_index_1_3,
226 'private' => $no_index_1_3,
227
228 'keywords' => { list => { value => \&string } },
229
230 'resources' => {
231 'map' => { license => { value => \&url },
232 homepage => { value => \&url },
233 bugtracker => { value => \&url },
234 repository => { value => \&url },
235 ':key' => { value => \&string, name => \&custom_1 },
236 }
237 },
238
239 # additional user defined key/value pairs
240 # note we can only validate the key name, as the structure is user defined
241 ':key' => { name => \&string, value => \&anything },
242},
243
244'1.3' => {
245 'meta-spec' => {
246 mandatory => 1,
247 'map' => {
248 version => { mandatory => 1, value => \&version},
249 url => { mandatory => 1, value => \&urlspec },
250 ':key' => { name => \&string, value => \&anything },
251 },
252 },
253
254 'name' => { mandatory => 1, value => \&string },
255 'version' => { mandatory => 1, value => \&version },
256 'abstract' => { mandatory => 1, value => \&string },
257 'author' => { mandatory => 1, list => { value => \&string } },
258 'license' => { mandatory => 1, value => \&license },
259 'generated_by' => { mandatory => 1, value => \&string },
260
261 'distribution_type' => { value => \&string },
262 'dynamic_config' => { value => \&boolean },
263
264 'requires' => $module_map1,
265 'recommends' => $module_map1,
266 'build_requires' => $module_map1,
267 'conflicts' => $module_map2,
268
269 'optional_features' => {
270 'map' => {
271 ':key' => { name => \&string,
272 'map' => { description => { value => \&string },
273 requires => $module_map1,
274 recommends => $module_map1,
275 build_requires => $module_map1,
276 conflicts => $module_map2,
277 ':key' => { name => \&string, value => \&anything },
278 }
279 }
280 }
281 },
282
283 'provides' => {
284 'map' => {
285 ':key' => { name => \&module,
286 'map' => {
287 file => { mandatory => 1, value => \&file },
288 version => { value => \&version },
289 ':key' => { name => \&string, value => \&anything },
290 }
291 }
292 }
293 },
294
295
296 'no_index' => $no_index_1_3,
297 'private' => $no_index_1_3,
298
299 'keywords' => { list => { value => \&string } },
300
301 'resources' => {
302 'map' => { license => { value => \&url },
303 homepage => { value => \&url },
304 bugtracker => { value => \&url },
305 repository => { value => \&url },
306 ':key' => { value => \&string, name => \&custom_1 },
307 }
308 },
309
310 # additional user defined key/value pairs
311 # note we can only validate the key name, as the structure is user defined
312 ':key' => { name => \&string, value => \&anything },
313},
314
315# v1.2 is misleading, it seems to assume that a number of fields where created
316# within v1.1, when they were created within v1.2. This may have been an
317# original mistake, and that a v1.1 was retro fitted into the timeline, when
318# v1.2 was originally slated as v1.1. But I could be wrong ;)
319'1.2' => {
320 'meta-spec' => {
321 mandatory => 1,
322 'map' => {
323 version => { mandatory => 1, value => \&version},
324 url => { mandatory => 1, value => \&urlspec },
325 ':key' => { name => \&string, value => \&anything },
326 },
327 },
328
329
330 'name' => { mandatory => 1, value => \&string },
331 'version' => { mandatory => 1, value => \&version },
332 'license' => { mandatory => 1, value => \&license },
333 'generated_by' => { mandatory => 1, value => \&string },
334 'author' => { mandatory => 1, list => { value => \&string } },
335 'abstract' => { mandatory => 1, value => \&string },
336
337 'distribution_type' => { value => \&string },
338 'dynamic_config' => { value => \&boolean },
339
340 'keywords' => { list => { value => \&string } },
341
342 'private' => $no_index_1_2,
343 '$no_index' => $no_index_1_2,
344
345 'requires' => $module_map1,
346 'recommends' => $module_map1,
347 'build_requires' => $module_map1,
348 'conflicts' => $module_map2,
349
350 'optional_features' => {
351 'map' => {
352 ':key' => { name => \&string,
353 'map' => { description => { value => \&string },
354 requires => $module_map1,
355 recommends => $module_map1,
356 build_requires => $module_map1,
357 conflicts => $module_map2,
358 ':key' => { name => \&string, value => \&anything },
359 }
360 }
361 }
362 },
363
364 'provides' => {
365 'map' => {
366 ':key' => { name => \&module,
367 'map' => {
368 file => { mandatory => 1, value => \&file },
369 version => { value => \&version },
370 ':key' => { name => \&string, value => \&anything },
371 }
372 }
373 }
374 },
375
376 'resources' => {
377 'map' => { license => { value => \&url },
378 homepage => { value => \&url },
379 bugtracker => { value => \&url },
380 repository => { value => \&url },
381 ':key' => { value => \&string, name => \&custom_1 },
382 }
383 },
384
385 # additional user defined key/value pairs
386 # note we can only validate the key name, as the structure is user defined
387 ':key' => { name => \&string, value => \&anything },
388},
389
390# note that the 1.1 spec only specifies 'version' as mandatory
391'1.1' => {
392 'name' => { value => \&string },
393 'version' => { mandatory => 1, value => \&version },
394 'license' => { value => \&license },
395 'generated_by' => { value => \&string },
396
397 'license_uri' => { value => \&url },
398 'distribution_type' => { value => \&string },
399 'dynamic_config' => { value => \&boolean },
400
401 'private' => $no_index_1_1,
402
403 'requires' => $module_map1,
404 'recommends' => $module_map1,
405 'build_requires' => $module_map1,
406 'conflicts' => $module_map2,
407
408 # additional user defined key/value pairs
409 # note we can only validate the key name, as the structure is user defined
410 ':key' => { name => \&string, value => \&anything },
411},
412
413# note that the 1.0 spec doesn't specify optional or mandatory fields
414# but we will treat version as mandatory since otherwise META 1.0 is
415# completely arbitrary and pointless
416'1.0' => {
417 'name' => { value => \&string },
418 'version' => { mandatory => 1, value => \&version },
419 'license' => { value => \&license },
420 'generated_by' => { value => \&string },
421
422 'license_uri' => { value => \&url },
423 'distribution_type' => { value => \&string },
424 'dynamic_config' => { value => \&boolean },
425
426 'requires' => $module_map1,
427 'recommends' => $module_map1,
428 'build_requires' => $module_map1,
429 'conflicts' => $module_map2,
430
431 # additional user defined key/value pairs
432 # note we can only validate the key name, as the structure is user defined
433 ':key' => { name => \&string, value => \&anything },
434},
435);
436
437#--------------------------------------------------------------------------#
438# Code
439#--------------------------------------------------------------------------#
440
441#pod =method new
442#pod
443#pod my $cmv = CPAN::Meta::Validator->new( $struct )
444#pod
445#pod The constructor must be passed a metadata structure.
446#pod
447#pod =cut
448
449
# spent 2µs within CPAN::Meta::Validator::new which was called: # once (2µs+0s) by CPAN::Meta::_new at line 231 of CPAN/Meta.pm
sub new {
4501200ns my ($class,$data) = @_;
451
452 # create an attributes hash
453 my $self = {
454 'data' => $data,
45521µs 'spec' => eval { $data->{'meta-spec'}{'version'} } || "1.0",
456 'errors' => undef,
457 };
458
459 # create the object
46012µs return bless $self, $class;
461}
462
463#pod =method is_valid
464#pod
465#pod if ( $cmv->is_valid ) {
466#pod ...
467#pod }
468#pod
469#pod Returns a boolean value indicating whether the metadata provided
470#pod is valid.
471#pod
472#pod =cut
473
474
# spent 64µs (5+59) within CPAN::Meta::Validator::is_valid which was called: # once (5µs+59µs) by CPAN::Meta::_new at line 232 of CPAN/Meta.pm
sub is_valid {
4751100ns my $self = shift;
4761900ns my $data = $self->{data};
4771200ns my $spec_version = $self->{spec};
47811µs158µs $self->check_map($definitions{$spec_version},$data);
# spent 58µs making 1 call to CPAN::Meta::Validator::check_map
47912µs1800ns return ! $self->errors;
# spent 800ns making 1 call to CPAN::Meta::Validator::errors
480}
481
482#pod =method errors
483#pod
484#pod warn( join "\n", $cmv->errors );
485#pod
486#pod Returns a list of errors seen during validation.
487#pod
488#pod =cut
489
490
# spent 800ns within CPAN::Meta::Validator::errors which was called: # once (800ns+0s) by CPAN::Meta::Validator::is_valid at line 479
sub errors {
4911100ns my $self = shift;
49211µs return () unless(defined $self->{errors});
493 return @{$self->{errors}};
494}
495
496#pod =begin :internals
497#pod
498#pod =head2 Check Methods
499#pod
500#pod =over
501#pod
502#pod =item *
503#pod
504#pod check_map($spec,$data)
505#pod
506#pod Checks whether a map (or hash) part of the data structure conforms to the
507#pod appropriate specification definition.
508#pod
509#pod =item *
510#pod
511#pod check_list($spec,$data)
512#pod
513#pod Checks whether a list (or array) part of the data structure conforms to
514#pod the appropriate specification definition.
515#pod
516#pod =item *
517#pod
518#pod =back
519#pod
520#pod =cut
521
5221400nsmy $spec_error = "Missing validation action in specification. "
523 . "Must be one of 'map', 'list', or 'value'";
524
525
# spent 58µs (32+26) within CPAN::Meta::Validator::check_map which was called 2 times, avg 29µs/call: # once (26µs+32µs) by CPAN::Meta::Validator::is_valid at line 478 # once (6µs+-6µs) by CPAN::Meta::Validator::check_map at line 549
sub check_map {
5262200ns my ($self,$spec,$data) = @_;
527
5282600ns if(ref($spec) ne 'HASH') {
529 $self->_error( "Unknown META specification, cannot validate." );
530 return;
531 }
532
5332200ns if(ref($data) ne 'HASH') {
534 $self->_error( "Expected a map structure from string or file." );
535 return;
536 }
537
53823µs for my $key (keys %$spec) {
539202µs next unless($spec->{$key}->{mandatory});
540101µs next if(defined $data->{$key});
541 push @{$self->{stack}}, $key;
542 $self->_error( "Missing mandatory field, '$key'" );
543 pop @{$self->{stack}};
544 }
545
54624µs for my $key (keys %$data) {
547102µs push @{$self->{stack}}, $key;
548102µs if($spec->{$key}) {
549109µs1026µs if($spec->{$key}{value}) {
# spent 10µs making 2 calls to CPAN::Meta::Validator::version, avg 5µs/call # spent 6µs making 2 calls to CPAN::Meta::Validator::check_list, avg 3µs/call # spent 4µs making 1 call to CPAN::Meta::Validator::release_status # spent 4µs making 1 call to CPAN::Meta::Validator::boolean # spent 2µs making 3 calls to CPAN::Meta::Validator::string, avg 800ns/call # spent 13µs making 1 call to CPAN::Meta::Validator::check_map, recursion: max depth 1, sum of overlapping time 13µs
550 $spec->{$key}{value}->($self,$key,$data->{$key});
551 } elsif($spec->{$key}{'map'}) {
552 $self->check_map($spec->{$key}{'map'},$data->{$key});
553 } elsif($spec->{$key}{'list'}) {
554 $self->check_list($spec->{$key}{'list'},$data->{$key});
555 } else {
556 $self->_error( "$spec_error for '$key'" );
557 }
558
559 } elsif ($spec->{':key'}) {
560 $spec->{':key'}{name}->($self,$key,$key);
561 if($spec->{':key'}{value}) {
562 $spec->{':key'}{value}->($self,$key,$data->{$key});
563 } elsif($spec->{':key'}{'map'}) {
564 $self->check_map($spec->{':key'}{'map'},$data->{$key});
565 } elsif($spec->{':key'}{'list'}) {
566 $self->check_list($spec->{':key'}{'list'},$data->{$key});
567 } else {
568 $self->_error( "$spec_error for ':key'" );
569 }
570
571
572 } else {
573 $self->_error( "Unknown key, '$key', found in map structure" );
574 }
575103µs pop @{$self->{stack}};
576 }
577}
578
579
# spent 6µs (4+2) within CPAN::Meta::Validator::check_list which was called 2 times, avg 3µs/call: # 2 times (4µs+2µs) by CPAN::Meta::Validator::check_map at line 549, avg 3µs/call
sub check_list {
5802200ns my ($self,$spec,$data) = @_;
581
5822400ns if(ref($data) ne 'ARRAY') {
583 $self->_error( "Expected a list structure" );
584 return;
585 }
586
5872300ns if(defined $spec->{mandatory}) {
588 if(!defined $data->[0]) {
589 $self->_error( "Missing entries from mandatory list" );
590 }
591 }
592
59323µs for my $value (@$data) {
5941200ns push @{$self->{stack}}, $value || "<undef>";
59511µs12µs if(defined $spec->{value}) {
# spent 2µs making 1 call to CPAN::Meta::Validator::license
596 $spec->{value}->($self,'list',$value);
597 } elsif(defined $spec->{'map'}) {
598 $self->check_map($spec->{'map'},$value);
599 } elsif(defined $spec->{'list'}) {
600 $self->check_list($spec->{'list'},$value);
601 } elsif ($spec->{':key'}) {
602 $self->check_map($spec,$value);
603 } else {
604 $self->_error( "$spec_error associated with '$self->{stack}[-2]'" );
605 }
6061300ns pop @{$self->{stack}};
607 }
608}
609
610#pod =head2 Validator Methods
611#pod
612#pod =over
613#pod
614#pod =item *
615#pod
616#pod header($self,$key,$value)
617#pod
618#pod Validates that the header is valid.
619#pod
620#pod Note: No longer used as we now read the data structure, not the file.
621#pod
622#pod =item *
623#pod
624#pod url($self,$key,$value)
625#pod
626#pod Validates that a given value is in an acceptable URL format
627#pod
628#pod =item *
629#pod
630#pod urlspec($self,$key,$value)
631#pod
632#pod Validates that the URL to a META specification is a known one.
633#pod
634#pod =item *
635#pod
636#pod string_or_undef($self,$key,$value)
637#pod
638#pod Validates that the value is either a string or an undef value. Bit of a
639#pod catchall function for parts of the data structure that are completely user
640#pod defined.
641#pod
642#pod =item *
643#pod
644#pod string($self,$key,$value)
645#pod
646#pod Validates that a string exists for the given key.
647#pod
648#pod =item *
649#pod
650#pod file($self,$key,$value)
651#pod
652#pod Validate that a file is passed for the given key. This may be made more
653#pod thorough in the future. For now it acts like \&string.
654#pod
655#pod =item *
656#pod
657#pod exversion($self,$key,$value)
658#pod
659#pod Validates a list of versions, e.g. '<= 5, >=2, ==3, !=4, >1, <6, 0'.
660#pod
661#pod =item *
662#pod
663#pod version($self,$key,$value)
664#pod
665#pod Validates a single version string. Versions of the type '5.8.8' and '0.00_00'
666#pod are both valid. A leading 'v' like 'v1.2.3' is also valid.
667#pod
668#pod =item *
669#pod
670#pod boolean($self,$key,$value)
671#pod
672#pod Validates for a boolean value: a defined value that is either "1" or "0" or
673#pod stringifies to those values.
674#pod
675#pod =item *
676#pod
677#pod license($self,$key,$value)
678#pod
679#pod Validates that a value is given for the license. Returns 1 if an known license
680#pod type, or 2 if a value is given but the license type is not a recommended one.
681#pod
682#pod =item *
683#pod
684#pod custom_1($self,$key,$value)
685#pod
686#pod Validates that the given key is in CamelCase, to indicate a user defined
687#pod keyword and only has characters in the class [-_a-zA-Z]. In version 1.X
688#pod of the spec, this was only explicitly stated for 'resources'.
689#pod
690#pod =item *
691#pod
692#pod custom_2($self,$key,$value)
693#pod
694#pod Validates that the given key begins with 'x_' or 'X_', to indicate a user
695#pod defined keyword and only has characters in the class [-_a-zA-Z]
696#pod
697#pod =item *
698#pod
699#pod identifier($self,$key,$value)
700#pod
701#pod Validates that key is in an acceptable format for the META specification,
702#pod for an identifier, i.e. any that matches the regular expression
703#pod qr/[a-z][a-z_]/i.
704#pod
705#pod =item *
706#pod
707#pod module($self,$key,$value)
708#pod
709#pod Validates that a given key is in an acceptable module name format, e.g.
710#pod 'Test::CPAN::Meta::Version'.
711#pod
712#pod =back
713#pod
714#pod =end :internals
715#pod
716#pod =cut
717
718sub header {
719 my ($self,$key,$value) = @_;
720 if(defined $value) {
721 return 1 if($value && $value =~ /^--- #YAML:1.0/);
722 }
723 $self->_error( "file does not have a valid YAML header." );
724 return 0;
725}
726
727
# spent 4µs (3+2) within CPAN::Meta::Validator::release_status which was called: # once (3µs+2µs) by CPAN::Meta::Validator::check_map at line 549
sub release_status {
7281200ns my ($self,$key,$value) = @_;
7291100ns if(defined $value) {
7301200ns my $version = $self->{data}{version} || '';
73112µs1600ns if ( $version =~ /_/ ) {
# spent 600ns making 1 call to CPAN::Meta::Validator::CORE:match
732 return 1 if ( $value =~ /\A(?:testing|unstable)\z/ );
733 $self->_error( "'$value' for '$key' is invalid for version '$version'" );
734 }
735 else {
73613µs1900ns return 1 if ( $value =~ /\A(?:stable|testing|unstable)\z/ );
# spent 900ns making 1 call to CPAN::Meta::Validator::CORE:match
737 $self->_error( "'$value' for '$key' is invalid" );
738 }
739 }
740 else {
741 $self->_error( "'$key' is not defined" );
742 }
743 return 0;
744}
745
746# _uri_split taken from URI::Split by Gisle Aas, Copyright 2003
747sub _uri_split {
748 return $_[0] =~ m,(?:([^:/?#]+):)?(?://([^/?#]*))?([^?#]*)(?:\?([^#]*))?(?:#(.*))?,;
749}
750
751sub url {
752 my ($self,$key,$value) = @_;
753 if(defined $value) {
754 my ($scheme, $auth, $path, $query, $frag) = _uri_split($value);
755 unless ( defined $scheme && length $scheme ) {
756 $self->_error( "'$value' for '$key' does not have a URL scheme" );
757 return 0;
758 }
759 unless ( defined $auth && length $auth ) {
760 $self->_error( "'$value' for '$key' does not have a URL authority" );
761 return 0;
762 }
763 return 1;
764 }
765 $value ||= '';
766 $self->_error( "'$value' for '$key' is not a valid URL." );
767 return 0;
768}
769
770sub urlspec {
771 my ($self,$key,$value) = @_;
772 if(defined $value) {
773 return 1 if($value && $known_specs{$self->{spec}} eq $value);
774 if($value && $known_urls{$value}) {
775 $self->_error( 'META specification URL does not match version' );
776 return 0;
777 }
778 }
779 $self->_error( 'Unknown META specification' );
780 return 0;
781}
782
783sub anything { return 1 }
784
785
# spent 2µs within CPAN::Meta::Validator::string which was called 3 times, avg 800ns/call: # 3 times (2µs+0s) by CPAN::Meta::Validator::check_map at line 549, avg 800ns/call
sub string {
7863600ns my ($self,$key,$value) = @_;
7873400ns if(defined $value) {
78833µs return 1 if($value || $value =~ /^0$/);
789 }
790 $self->_error( "value is an undefined string" );
791 return 0;
792}
793
794sub string_or_undef {
795 my ($self,$key,$value) = @_;
796 return 1 unless(defined $value);
797 return 1 if($value || $value =~ /^0$/);
798 $self->_error( "No string defined for '$key'" );
799 return 0;
800}
801
802sub file {
803 my ($self,$key,$value) = @_;
804 return 1 if(defined $value);
805 $self->_error( "No file defined for '$key'" );
806 return 0;
807}
808
809sub exversion {
810 my ($self,$key,$value) = @_;
811 if(defined $value && ($value || $value =~ /0/)) {
812 my $pass = 1;
813 for(split(",",$value)) { $self->version($key,$_) or ($pass = 0); }
814 return $pass;
815 }
816 $value = '<undef>' unless(defined $value);
817 $self->_error( "'$value' for '$key' is not a valid version." );
818 return 0;
819}
820
821
# spent 10µs (4+6) within CPAN::Meta::Validator::version which was called 2 times, avg 5µs/call: # 2 times (4µs+6µs) by CPAN::Meta::Validator::check_map at line 549, avg 5µs/call
sub version {
8222400ns my ($self,$key,$value) = @_;
8232100ns if(defined $value) {
8242100ns return 0 unless($value || $value =~ /0/);
825211µs26µs return 1 if($value =~ /^\s*((<|<=|>=|>|!=|==)\s*)?v?\d+((\.\d+((_|\.)\d+)?)?)/);
# spent 6µs making 2 calls to CPAN::Meta::Validator::CORE:match, avg 3µs/call
826 } else {
827 $value = '<undef>';
828 }
829 $self->_error( "'$value' for '$key' is not a valid version." );
830 return 0;
831}
832
833
# spent 4µs (2+1) within CPAN::Meta::Validator::boolean which was called: # once (2µs+1µs) by CPAN::Meta::Validator::check_map at line 549
sub boolean {
8341300ns my ($self,$key,$value) = @_;
8351100ns if(defined $value) {
83614µs11µs return 1 if($value =~ /^(0|1)$/);
# spent 1µs making 1 call to CPAN::Meta::Validator::CORE:match
837 } else {
838 $value = '<undef>';
839 }
840 $self->_error( "'$value' for '$key' is not a boolean value." );
841 return 0;
842}
843
84413µsmy %v1_licenses = (
845 'perl' => 'http://dev.perl.org/licenses/',
846 'gpl' => 'http://www.opensource.org/licenses/gpl-license.php',
847 'apache' => 'http://apache.org/licenses/LICENSE-2.0',
848 'artistic' => 'http://opensource.org/licenses/artistic-license.php',
849 'artistic_2' => 'http://opensource.org/licenses/artistic-license-2.0.php',
850 'lgpl' => 'http://www.opensource.org/licenses/lgpl-license.php',
851 'bsd' => 'http://www.opensource.org/licenses/bsd-license.php',
852 'gpl' => 'http://www.opensource.org/licenses/gpl-license.php',
853 'mit' => 'http://opensource.org/licenses/mit-license.php',
854 'mozilla' => 'http://opensource.org/licenses/mozilla1.1.php',
855 'open_source' => undef,
856 'unrestricted' => undef,
857 'restrictive' => undef,
858 'unknown' => undef,
859);
860
86116µsmy %v2_licenses = map { $_ => 1 } qw(
862 agpl_3
863 apache_1_1
864 apache_2_0
865 artistic_1
866 artistic_2
867 bsd
868 freebsd
869 gfdl_1_2
870 gfdl_1_3
871 gpl_1
872 gpl_2
873 gpl_3
874 lgpl_2_1
875 lgpl_3_0
876 mit
877 mozilla_1_0
878 mozilla_1_1
879 openssl
880 perl_5
881 qpl_1_0
882 ssleay
883 sun
884 zlib
885 open_source
886 restricted
887 unrestricted
888 unknown
889);
890
891
# spent 2µs within CPAN::Meta::Validator::license which was called: # once (2µs+0s) by CPAN::Meta::Validator::check_list at line 595
sub license {
8921300ns my ($self,$key,$value) = @_;
8931400ns my $licenses = $self->{spec} < 2 ? \%v1_licenses : \%v2_licenses;
89412µs if(defined $value) {
895 return 1 if($value && exists $licenses->{$value});
896 } else {
897 $value = '<undef>';
898 }
899 $self->_error( "License '$value' is invalid" );
900 return 0;
901}
902
903sub custom_1 {
904 my ($self,$key) = @_;
905 if(defined $key) {
906 # a valid user defined key should be alphabetic
907 # and contain at least one capital case letter.
908 return 1 if($key && $key =~ /^[_a-z]+$/i && $key =~ /[A-Z]/);
909 } else {
910 $key = '<undef>';
911 }
912 $self->_error( "Custom resource '$key' must be in CamelCase." );
913 return 0;
914}
915
916sub custom_2 {
917 my ($self,$key) = @_;
918 if(defined $key) {
919 return 1 if($key && $key =~ /^x_/i); # user defined
920 } else {
921 $key = '<undef>';
922 }
923 $self->_error( "Custom key '$key' must begin with 'x_' or 'X_'." );
924 return 0;
925}
926
927sub identifier {
928 my ($self,$key) = @_;
929 if(defined $key) {
930 return 1 if($key && $key =~ /^([a-z][_a-z]+)$/i); # spec 2.0 defined
931 } else {
932 $key = '<undef>';
933 }
934 $self->_error( "Key '$key' is not a legal identifier." );
935 return 0;
936}
937
938sub module {
939 my ($self,$key) = @_;
940 if(defined $key) {
941 return 1 if($key && $key =~ /^[A-Za-z0-9_]+(::[A-Za-z0-9_]+)*$/);
942 } else {
943 $key = '<undef>';
944 }
945 $self->_error( "Key '$key' is not a legal module name." );
946 return 0;
947}
948
9491500nsmy @valid_phases = qw/ configure build test runtime develop /;
950sub phase {
951 my ($self,$key) = @_;
952 if(defined $key) {
953 return 1 if( length $key && grep { $key eq $_ } @valid_phases );
954 return 1 if $key =~ /x_/i;
955 } else {
956 $key = '<undef>';
957 }
958 $self->_error( "Key '$key' is not a legal phase." );
959 return 0;
960}
961
9621300nsmy @valid_relations = qw/ requires recommends suggests conflicts /;
963sub relation {
964 my ($self,$key) = @_;
965 if(defined $key) {
966 return 1 if( length $key && grep { $key eq $_ } @valid_relations );
967 return 1 if $key =~ /x_/i;
968 } else {
969 $key = '<undef>';
970 }
971 $self->_error( "Key '$key' is not a legal prereq relationship." );
972 return 0;
973}
974
975sub _error {
976 my $self = shift;
977 my $mess = shift;
978
979 $mess .= ' ('.join(' -> ',@{$self->{stack}}).')' if($self->{stack});
980 $mess .= " [Validation: $self->{spec}]";
981
982 push @{$self->{errors}}, $mess;
983}
984
985154µs1;
986
987# ABSTRACT: validate CPAN distribution metadata structures
988
989=pod
990
991=encoding UTF-8
992
993=head1 NAME
994
995CPAN::Meta::Validator - validate CPAN distribution metadata structures
996
997=head1 VERSION
998
999version 2.150010
1000
1001=head1 SYNOPSIS
1002
1003 my $struct = decode_json_file('META.json');
1004
1005 my $cmv = CPAN::Meta::Validator->new( $struct );
1006
1007 unless ( $cmv->is_valid ) {
1008 my $msg = "Invalid META structure. Errors found:\n";
1009 $msg .= join( "\n", $cmv->errors );
1010 die $msg;
1011 }
1012
1013=head1 DESCRIPTION
1014
1015This module validates a CPAN Meta structure against the version of the
1016the specification claimed in the C<meta-spec> field of the structure.
1017
1018=head1 METHODS
1019
1020=head2 new
1021
1022 my $cmv = CPAN::Meta::Validator->new( $struct )
1023
1024The constructor must be passed a metadata structure.
1025
1026=head2 is_valid
1027
1028 if ( $cmv->is_valid ) {
1029 ...
1030 }
1031
1032Returns a boolean value indicating whether the metadata provided
1033is valid.
1034
1035=head2 errors
1036
1037 warn( join "\n", $cmv->errors );
1038
1039Returns a list of errors seen during validation.
1040
1041=begin :internals
1042
1043=head2 Check Methods
1044
1045=over
1046
1047=item *
1048
1049check_map($spec,$data)
1050
1051Checks whether a map (or hash) part of the data structure conforms to the
1052appropriate specification definition.
1053
1054=item *
1055
1056check_list($spec,$data)
1057
1058Checks whether a list (or array) part of the data structure conforms to
1059the appropriate specification definition.
1060
1061=item *
1062
1063=back
1064
1065=head2 Validator Methods
1066
1067=over
1068
1069=item *
1070
1071header($self,$key,$value)
1072
1073Validates that the header is valid.
1074
1075Note: No longer used as we now read the data structure, not the file.
1076
1077=item *
1078
1079url($self,$key,$value)
1080
1081Validates that a given value is in an acceptable URL format
1082
1083=item *
1084
1085urlspec($self,$key,$value)
1086
1087Validates that the URL to a META specification is a known one.
1088
1089=item *
1090
1091string_or_undef($self,$key,$value)
1092
1093Validates that the value is either a string or an undef value. Bit of a
1094catchall function for parts of the data structure that are completely user
1095defined.
1096
1097=item *
1098
1099string($self,$key,$value)
1100
1101Validates that a string exists for the given key.
1102
1103=item *
1104
1105file($self,$key,$value)
1106
1107Validate that a file is passed for the given key. This may be made more
1108thorough in the future. For now it acts like \&string.
1109
1110=item *
1111
1112exversion($self,$key,$value)
1113
1114Validates a list of versions, e.g. '<= 5, >=2, ==3, !=4, >1, <6, 0'.
1115
1116=item *
1117
1118version($self,$key,$value)
1119
1120Validates a single version string. Versions of the type '5.8.8' and '0.00_00'
1121are both valid. A leading 'v' like 'v1.2.3' is also valid.
1122
1123=item *
1124
1125boolean($self,$key,$value)
1126
1127Validates for a boolean value: a defined value that is either "1" or "0" or
1128stringifies to those values.
1129
1130=item *
1131
1132license($self,$key,$value)
1133
1134Validates that a value is given for the license. Returns 1 if an known license
1135type, or 2 if a value is given but the license type is not a recommended one.
1136
1137=item *
1138
1139custom_1($self,$key,$value)
1140
1141Validates that the given key is in CamelCase, to indicate a user defined
1142keyword and only has characters in the class [-_a-zA-Z]. In version 1.X
1143of the spec, this was only explicitly stated for 'resources'.
1144
1145=item *
1146
1147custom_2($self,$key,$value)
1148
1149Validates that the given key begins with 'x_' or 'X_', to indicate a user
1150defined keyword and only has characters in the class [-_a-zA-Z]
1151
1152=item *
1153
1154identifier($self,$key,$value)
1155
1156Validates that key is in an acceptable format for the META specification,
1157for an identifier, i.e. any that matches the regular expression
1158qr/[a-z][a-z_]/i.
1159
1160=item *
1161
1162module($self,$key,$value)
1163
1164Validates that a given key is in an acceptable module name format, e.g.
1165'Test::CPAN::Meta::Version'.
1166
1167=back
1168
1169=end :internals
1170
1171=for Pod::Coverage anything boolean check_list custom_1 custom_2 exversion file
1172identifier license module phase relation release_status string string_or_undef
1173url urlspec version header check_map
1174
1175=head1 BUGS
1176
1177Please report any bugs or feature using the CPAN Request Tracker.
1178Bugs can be submitted through the web interface at
1179L<http://rt.cpan.org/Dist/Display.html?Queue=CPAN-Meta>
1180
1181When submitting a bug or request, please include a test-file or a patch to an
1182existing test-file that illustrates the bug or desired feature.
1183
1184=head1 AUTHORS
1185
1186=over 4
1187
1188=item *
1189
1190David Golden <dagolden@cpan.org>
1191
1192=item *
1193
1194Ricardo Signes <rjbs@cpan.org>
1195
1196=item *
1197
1198Adam Kennedy <adamk@cpan.org>
1199
1200=back
1201
1202=head1 COPYRIGHT AND LICENSE
1203
1204This software is copyright (c) 2010 by David Golden, Ricardo Signes, Adam Kennedy and Contributors.
1205
1206This is free software; you can redistribute it and/or modify it under
1207the same terms as the Perl 5 programming language system itself.
1208
1209=cut
1210
1211__END__
 
# spent 9µs within CPAN::Meta::Validator::CORE:match which was called 5 times, avg 2µs/call: # 2 times (6µs+0s) by CPAN::Meta::Validator::version at line 825, avg 3µs/call # once (1µs+0s) by CPAN::Meta::Validator::boolean at line 836 # once (900ns+0s) by CPAN::Meta::Validator::release_status at line 736 # once (600ns+0s) by CPAN::Meta::Validator::release_status at line 731
sub CPAN::Meta::Validator::CORE:match; # opcode