| 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 | CPAN::Meta::Validator::check_map (recurses: max depth 1, inclusive time 13µs) |
| 5 | 4 | 1 | 9µs | 9µs | CPAN::Meta::Validator::CORE:match (opcode) |
| 1 | 1 | 1 | 6µs | 6µs | CPAN::Meta::Converter::BEGIN@1 |
| 1 | 1 | 1 | 5µs | 64µs | CPAN::Meta::Validator::is_valid |
| 2 | 1 | 1 | 4µs | 10µs | CPAN::Meta::Validator::version |
| 2 | 1 | 1 | 4µs | 6µs | CPAN::Meta::Validator::check_list |
| 1 | 1 | 1 | 3µs | 4µs | CPAN::Meta::Converter::BEGIN@2 |
| 1 | 1 | 1 | 3µs | 4µs | CPAN::Meta::Validator::release_status |
| 1 | 1 | 1 | 2µs | 10µs | CPAN::Meta::Converter::BEGIN@3 |
| 3 | 1 | 1 | 2µs | 2µs | CPAN::Meta::Validator::string |
| 1 | 1 | 1 | 2µs | 2µs | CPAN::Meta::Validator::license |
| 1 | 1 | 1 | 2µs | 4µs | CPAN::Meta::Validator::boolean |
| 1 | 1 | 1 | 2µs | 2µs | CPAN::Meta::Validator::new |
| 1 | 1 | 1 | 800ns | 800ns | CPAN::Meta::Validator::errors |
| 0 | 0 | 0 | 0s | 0s | CPAN::Meta::Validator::_error |
| 0 | 0 | 0 | 0s | 0s | CPAN::Meta::Validator::_uri_split |
| 0 | 0 | 0 | 0s | 0s | CPAN::Meta::Validator::anything |
| 0 | 0 | 0 | 0s | 0s | CPAN::Meta::Validator::custom_1 |
| 0 | 0 | 0 | 0s | 0s | CPAN::Meta::Validator::custom_2 |
| 0 | 0 | 0 | 0s | 0s | CPAN::Meta::Validator::exversion |
| 0 | 0 | 0 | 0s | 0s | CPAN::Meta::Validator::file |
| 0 | 0 | 0 | 0s | 0s | CPAN::Meta::Validator::header |
| 0 | 0 | 0 | 0s | 0s | CPAN::Meta::Validator::identifier |
| 0 | 0 | 0 | 0s | 0s | CPAN::Meta::Validator::module |
| 0 | 0 | 0 | 0s | 0s | CPAN::Meta::Validator::phase |
| 0 | 0 | 0 | 0s | 0s | CPAN::Meta::Validator::relation |
| 0 | 0 | 0 | 0s | 0s | CPAN::Meta::Validator::string_or_undef |
| 0 | 0 | 0 | 0s | 0s | CPAN::Meta::Validator::url |
| 0 | 0 | 0 | 0s | 0s | CPAN::Meta::Validator::urlspec |
| 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 |