Filename | /home/leont/perl5/perlbrew/perls/perl-5.32.0/lib/5.32.0/CPAN/Meta/Validator.pm |
Statements | Executed 153 statements in 2.41ms |
Calls | P | F | Exclusive Time |
Inclusive Time |
Subroutine |
---|---|---|---|---|---|
2 | 2 | 1 | 32µs | 58µs | check_map (recurses: max depth 1, inclusive time 13µs) | CPAN::Meta::Validator::
5 | 4 | 1 | 9µs | 9µs | CORE:match (opcode) | CPAN::Meta::Validator::
1 | 1 | 1 | 6µs | 6µs | BEGIN@1 | CPAN::Meta::Converter::
1 | 1 | 1 | 5µs | 64µs | is_valid | CPAN::Meta::Validator::
2 | 1 | 1 | 4µs | 10µs | version | CPAN::Meta::Validator::
2 | 1 | 1 | 4µs | 6µs | check_list | CPAN::Meta::Validator::
1 | 1 | 1 | 3µs | 4µs | BEGIN@2 | CPAN::Meta::Converter::
1 | 1 | 1 | 3µs | 4µs | release_status | CPAN::Meta::Validator::
1 | 1 | 1 | 2µs | 10µs | BEGIN@3 | CPAN::Meta::Converter::
3 | 1 | 1 | 2µs | 2µs | string | CPAN::Meta::Validator::
1 | 1 | 1 | 2µs | 2µs | license | CPAN::Meta::Validator::
1 | 1 | 1 | 2µs | 4µs | boolean | CPAN::Meta::Validator::
1 | 1 | 1 | 2µs | 2µs | new | CPAN::Meta::Validator::
1 | 1 | 1 | 800ns | 800ns | errors | CPAN::Meta::Validator::
0 | 0 | 0 | 0s | 0s | _error | CPAN::Meta::Validator::
0 | 0 | 0 | 0s | 0s | _uri_split | CPAN::Meta::Validator::
0 | 0 | 0 | 0s | 0s | anything | CPAN::Meta::Validator::
0 | 0 | 0 | 0s | 0s | custom_1 | CPAN::Meta::Validator::
0 | 0 | 0 | 0s | 0s | custom_2 | CPAN::Meta::Validator::
0 | 0 | 0 | 0s | 0s | exversion | CPAN::Meta::Validator::
0 | 0 | 0 | 0s | 0s | file | CPAN::Meta::Validator::
0 | 0 | 0 | 0s | 0s | header | CPAN::Meta::Validator::
0 | 0 | 0 | 0s | 0s | identifier | CPAN::Meta::Validator::
0 | 0 | 0 | 0s | 0s | module | CPAN::Meta::Validator::
0 | 0 | 0 | 0s | 0s | phase | CPAN::Meta::Validator::
0 | 0 | 0 | 0s | 0s | relation | CPAN::Meta::Validator::
0 | 0 | 0 | 0s | 0s | string_or_undef | CPAN::Meta::Validator::
0 | 0 | 0 | 0s | 0s | url | CPAN::Meta::Validator::
0 | 0 | 0 | 0s | 0s | urlspec | CPAN::Meta::Validator::
Line | State ments |
Time on line |
Calls | Time in subs |
Code |
---|---|---|---|---|---|
1 | 2 | 20µs | 1 | 6µ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 # spent 6µs making 1 call to CPAN::Meta::Converter::BEGIN@1 |
2 | 2 | 9µs | 2 | 4µ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 # spent 4µs making 1 call to CPAN::Meta::Converter::BEGIN@2
# spent 800ns making 1 call to strict::import |
3 | 2 | 2.18ms | 2 | 18µ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 # spent 10µs making 1 call to CPAN::Meta::Converter::BEGIN@3
# spent 8µs making 1 call to warnings::import |
4 | package CPAN::Meta::Validator; | ||||
5 | |||||
6 | 1 | 300ns | our $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 | |||||
37 | 1 | 1µs | my %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 | ); | ||||
44 | 1 | 3µs | my %known_urls = map {$known_specs{$_} => $_} keys %known_specs; | ||
45 | |||||
46 | 1 | 1µs | my $module_map1 = { 'map' => { ':key' => { name => \&module, value => \&exversion } } }; | ||
47 | |||||
48 | 1 | 500ns | my $module_map2 = { 'map' => { ':key' => { name => \&module, value => \&version } } }; | ||
49 | |||||
50 | 1 | 2µs | my $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 | |||||
59 | 1 | 2µs | my $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 | |||||
68 | 1 | 2µs | my $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 | |||||
77 | 1 | 700ns | my $no_index_1_1 = { | ||
78 | 'map' => { ':key' => { name => \&string, list => { value => \&string } }, | ||||
79 | } | ||||
80 | }; | ||||
81 | |||||
82 | 1 | 1µs | my $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 | |||||
96 | 1 | 57µs | my %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 | ||||
450 | 1 | 200ns | my ($class,$data) = @_; | ||
451 | |||||
452 | # create an attributes hash | ||||
453 | my $self = { | ||||
454 | 'data' => $data, | ||||
455 | 2 | 1µs | 'spec' => eval { $data->{'meta-spec'}{'version'} } || "1.0", | ||
456 | 'errors' => undef, | ||||
457 | }; | ||||
458 | |||||
459 | # create the object | ||||
460 | 1 | 2µ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 | ||||
475 | 1 | 100ns | my $self = shift; | ||
476 | 1 | 900ns | my $data = $self->{data}; | ||
477 | 1 | 200ns | my $spec_version = $self->{spec}; | ||
478 | 1 | 1µs | 1 | 58µs | $self->check_map($definitions{$spec_version},$data); # spent 58µs making 1 call to CPAN::Meta::Validator::check_map |
479 | 1 | 2µs | 1 | 800ns | 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 | ||||
491 | 1 | 100ns | my $self = shift; | ||
492 | 1 | 1µ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 | |||||
522 | 1 | 400ns | my $spec_error = "Missing validation action in specification. " | ||
523 | . "Must be one of 'map', 'list', or 'value'"; | ||||
524 | |||||
525 | sub check_map { | ||||
526 | 2 | 200ns | my ($self,$spec,$data) = @_; | ||
527 | |||||
528 | 2 | 600ns | if(ref($spec) ne 'HASH') { | ||
529 | $self->_error( "Unknown META specification, cannot validate." ); | ||||
530 | return; | ||||
531 | } | ||||
532 | |||||
533 | 2 | 200ns | if(ref($data) ne 'HASH') { | ||
534 | $self->_error( "Expected a map structure from string or file." ); | ||||
535 | return; | ||||
536 | } | ||||
537 | |||||
538 | 2 | 3µs | for my $key (keys %$spec) { | ||
539 | 20 | 2µs | next unless($spec->{$key}->{mandatory}); | ||
540 | 10 | 1µs | next if(defined $data->{$key}); | ||
541 | push @{$self->{stack}}, $key; | ||||
542 | $self->_error( "Missing mandatory field, '$key'" ); | ||||
543 | pop @{$self->{stack}}; | ||||
544 | } | ||||
545 | |||||
546 | 2 | 4µs | for my $key (keys %$data) { | ||
547 | 10 | 2µs | push @{$self->{stack}}, $key; | ||
548 | 10 | 2µs | if($spec->{$key}) { | ||
549 | 10 | 9µs | 10 | 26µ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 | } | ||||
575 | 10 | 3µ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 | ||||
580 | 2 | 200ns | my ($self,$spec,$data) = @_; | ||
581 | |||||
582 | 2 | 400ns | if(ref($data) ne 'ARRAY') { | ||
583 | $self->_error( "Expected a list structure" ); | ||||
584 | return; | ||||
585 | } | ||||
586 | |||||
587 | 2 | 300ns | if(defined $spec->{mandatory}) { | ||
588 | if(!defined $data->[0]) { | ||||
589 | $self->_error( "Missing entries from mandatory list" ); | ||||
590 | } | ||||
591 | } | ||||
592 | |||||
593 | 2 | 3µs | for my $value (@$data) { | ||
594 | 1 | 200ns | push @{$self->{stack}}, $value || "<undef>"; | ||
595 | 1 | 1µs | 1 | 2µ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 | } | ||||
606 | 1 | 300ns | 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 | |||||
718 | sub 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 | ||||
728 | 1 | 200ns | my ($self,$key,$value) = @_; | ||
729 | 1 | 100ns | if(defined $value) { | ||
730 | 1 | 200ns | my $version = $self->{data}{version} || ''; | ||
731 | 1 | 2µs | 1 | 600ns | 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 { | ||||
736 | 1 | 3µs | 1 | 900ns | 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 | ||||
747 | sub _uri_split { | ||||
748 | return $_[0] =~ m,(?:([^:/?#]+):)?(?://([^/?#]*))?([^?#]*)(?:\?([^#]*))?(?:#(.*))?,; | ||||
749 | } | ||||
750 | |||||
751 | sub 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 | |||||
770 | sub 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 | |||||
783 | sub 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 | ||||
786 | 3 | 600ns | my ($self,$key,$value) = @_; | ||
787 | 3 | 400ns | if(defined $value) { | ||
788 | 3 | 3µs | return 1 if($value || $value =~ /^0$/); | ||
789 | } | ||||
790 | $self->_error( "value is an undefined string" ); | ||||
791 | return 0; | ||||
792 | } | ||||
793 | |||||
794 | sub 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 | |||||
802 | sub 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 | |||||
809 | sub 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 | ||||
822 | 2 | 400ns | my ($self,$key,$value) = @_; | ||
823 | 2 | 100ns | if(defined $value) { | ||
824 | 2 | 100ns | return 0 unless($value || $value =~ /0/); | ||
825 | 2 | 11µs | 2 | 6µ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 | ||||
834 | 1 | 300ns | my ($self,$key,$value) = @_; | ||
835 | 1 | 100ns | if(defined $value) { | ||
836 | 1 | 4µs | 1 | 1µ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 | |||||
844 | 1 | 3µs | my %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 | |||||
861 | 1 | 6µs | my %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 | ||||
892 | 1 | 300ns | my ($self,$key,$value) = @_; | ||
893 | 1 | 400ns | my $licenses = $self->{spec} < 2 ? \%v1_licenses : \%v2_licenses; | ||
894 | 1 | 2µ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 | |||||
903 | sub 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 | |||||
916 | sub 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 | |||||
927 | sub 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 | |||||
938 | sub 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 | |||||
949 | 1 | 500ns | my @valid_phases = qw/ configure build test runtime develop /; | ||
950 | sub 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 | |||||
962 | 1 | 300ns | my @valid_relations = qw/ requires recommends suggests conflicts /; | ||
963 | sub 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 | |||||
975 | sub _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 | |||||
985 | 1 | 54µs | 1; | ||
986 | |||||
987 | # ABSTRACT: validate CPAN distribution metadata structures | ||||
988 | |||||
989 | =pod | ||||
990 | |||||
991 | =encoding UTF-8 | ||||
992 | |||||
993 | =head1 NAME | ||||
994 | |||||
995 | CPAN::Meta::Validator - validate CPAN distribution metadata structures | ||||
996 | |||||
997 | =head1 VERSION | ||||
998 | |||||
999 | version 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 | |||||
1015 | This module validates a CPAN Meta structure against the version of the | ||||
1016 | the 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 | |||||
1024 | The constructor must be passed a metadata structure. | ||||
1025 | |||||
1026 | =head2 is_valid | ||||
1027 | |||||
1028 | if ( $cmv->is_valid ) { | ||||
1029 | ... | ||||
1030 | } | ||||
1031 | |||||
1032 | Returns a boolean value indicating whether the metadata provided | ||||
1033 | is valid. | ||||
1034 | |||||
1035 | =head2 errors | ||||
1036 | |||||
1037 | warn( join "\n", $cmv->errors ); | ||||
1038 | |||||
1039 | Returns a list of errors seen during validation. | ||||
1040 | |||||
1041 | =begin :internals | ||||
1042 | |||||
1043 | =head2 Check Methods | ||||
1044 | |||||
1045 | =over | ||||
1046 | |||||
1047 | =item * | ||||
1048 | |||||
1049 | check_map($spec,$data) | ||||
1050 | |||||
1051 | Checks whether a map (or hash) part of the data structure conforms to the | ||||
1052 | appropriate specification definition. | ||||
1053 | |||||
1054 | =item * | ||||
1055 | |||||
1056 | check_list($spec,$data) | ||||
1057 | |||||
1058 | Checks whether a list (or array) part of the data structure conforms to | ||||
1059 | the appropriate specification definition. | ||||
1060 | |||||
1061 | =item * | ||||
1062 | |||||
1063 | =back | ||||
1064 | |||||
1065 | =head2 Validator Methods | ||||
1066 | |||||
1067 | =over | ||||
1068 | |||||
1069 | =item * | ||||
1070 | |||||
1071 | header($self,$key,$value) | ||||
1072 | |||||
1073 | Validates that the header is valid. | ||||
1074 | |||||
1075 | Note: No longer used as we now read the data structure, not the file. | ||||
1076 | |||||
1077 | =item * | ||||
1078 | |||||
1079 | url($self,$key,$value) | ||||
1080 | |||||
1081 | Validates that a given value is in an acceptable URL format | ||||
1082 | |||||
1083 | =item * | ||||
1084 | |||||
1085 | urlspec($self,$key,$value) | ||||
1086 | |||||
1087 | Validates that the URL to a META specification is a known one. | ||||
1088 | |||||
1089 | =item * | ||||
1090 | |||||
1091 | string_or_undef($self,$key,$value) | ||||
1092 | |||||
1093 | Validates that the value is either a string or an undef value. Bit of a | ||||
1094 | catchall function for parts of the data structure that are completely user | ||||
1095 | defined. | ||||
1096 | |||||
1097 | =item * | ||||
1098 | |||||
1099 | string($self,$key,$value) | ||||
1100 | |||||
1101 | Validates that a string exists for the given key. | ||||
1102 | |||||
1103 | =item * | ||||
1104 | |||||
1105 | file($self,$key,$value) | ||||
1106 | |||||
1107 | Validate that a file is passed for the given key. This may be made more | ||||
1108 | thorough in the future. For now it acts like \&string. | ||||
1109 | |||||
1110 | =item * | ||||
1111 | |||||
1112 | exversion($self,$key,$value) | ||||
1113 | |||||
1114 | Validates a list of versions, e.g. '<= 5, >=2, ==3, !=4, >1, <6, 0'. | ||||
1115 | |||||
1116 | =item * | ||||
1117 | |||||
1118 | version($self,$key,$value) | ||||
1119 | |||||
1120 | Validates a single version string. Versions of the type '5.8.8' and '0.00_00' | ||||
1121 | are both valid. A leading 'v' like 'v1.2.3' is also valid. | ||||
1122 | |||||
1123 | =item * | ||||
1124 | |||||
1125 | boolean($self,$key,$value) | ||||
1126 | |||||
1127 | Validates for a boolean value: a defined value that is either "1" or "0" or | ||||
1128 | stringifies to those values. | ||||
1129 | |||||
1130 | =item * | ||||
1131 | |||||
1132 | license($self,$key,$value) | ||||
1133 | |||||
1134 | Validates that a value is given for the license. Returns 1 if an known license | ||||
1135 | type, or 2 if a value is given but the license type is not a recommended one. | ||||
1136 | |||||
1137 | =item * | ||||
1138 | |||||
1139 | custom_1($self,$key,$value) | ||||
1140 | |||||
1141 | Validates that the given key is in CamelCase, to indicate a user defined | ||||
1142 | keyword and only has characters in the class [-_a-zA-Z]. In version 1.X | ||||
1143 | of the spec, this was only explicitly stated for 'resources'. | ||||
1144 | |||||
1145 | =item * | ||||
1146 | |||||
1147 | custom_2($self,$key,$value) | ||||
1148 | |||||
1149 | Validates that the given key begins with 'x_' or 'X_', to indicate a user | ||||
1150 | defined keyword and only has characters in the class [-_a-zA-Z] | ||||
1151 | |||||
1152 | =item * | ||||
1153 | |||||
1154 | identifier($self,$key,$value) | ||||
1155 | |||||
1156 | Validates that key is in an acceptable format for the META specification, | ||||
1157 | for an identifier, i.e. any that matches the regular expression | ||||
1158 | qr/[a-z][a-z_]/i. | ||||
1159 | |||||
1160 | =item * | ||||
1161 | |||||
1162 | module($self,$key,$value) | ||||
1163 | |||||
1164 | Validates 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 | ||||
1172 | identifier license module phase relation release_status string string_or_undef | ||||
1173 | url urlspec version header check_map | ||||
1174 | |||||
1175 | =head1 BUGS | ||||
1176 | |||||
1177 | Please report any bugs or feature using the CPAN Request Tracker. | ||||
1178 | Bugs can be submitted through the web interface at | ||||
1179 | L<http://rt.cpan.org/Dist/Display.html?Queue=CPAN-Meta> | ||||
1180 | |||||
1181 | When submitting a bug or request, please include a test-file or a patch to an | ||||
1182 | existing test-file that illustrates the bug or desired feature. | ||||
1183 | |||||
1184 | =head1 AUTHORS | ||||
1185 | |||||
1186 | =over 4 | ||||
1187 | |||||
1188 | =item * | ||||
1189 | |||||
1190 | David Golden <dagolden@cpan.org> | ||||
1191 | |||||
1192 | =item * | ||||
1193 | |||||
1194 | Ricardo Signes <rjbs@cpan.org> | ||||
1195 | |||||
1196 | =item * | ||||
1197 | |||||
1198 | Adam Kennedy <adamk@cpan.org> | ||||
1199 | |||||
1200 | =back | ||||
1201 | |||||
1202 | =head1 COPYRIGHT AND LICENSE | ||||
1203 | |||||
1204 | This software is copyright (c) 2010 by David Golden, Ricardo Signes, Adam Kennedy and Contributors. | ||||
1205 | |||||
1206 | This is free software; you can redistribute it and/or modify it under | ||||
1207 | the 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 |