File Coverage

blib/lib/PGXN/Meta/Validator.pm
Criterion Covered Total %
statement 202 245 82.4
branch 96 144 66.6
condition 40 68 58.8
subroutine 33 38 86.8
pod 27 27 100.0
total 398 522 76.2


line stmt bran cond sub pod time code
1             package PGXN::Meta::Validator;
2              
3 2     2   81616 use 5.010;
  2         7  
  2         71  
4 2     2   10 use strict;
  2         4  
  2         141  
5 2     2   11 use warnings;
  2         8  
  2         53  
6 2     2   1747 use SemVer;
  2         13069  
  2         12  
7 2     2   5765 use JSON;
  2         34273  
  2         16  
8 2     2   455 use Carp qw(croak);
  2         3  
  2         11030  
9             our $VERSION = v0.16.0;
10              
11             =head1 Name
12              
13             PGXN::Meta::Validator - Validate PGXN distribution metadata structures
14              
15             =head1 Synopsis
16              
17             my $struct = decode_json_file('META.json');
18              
19             my $pmv = PGXN::Meta::Validator->new( $struct );
20              
21             unless ( $pmv->is_valid ) {
22             my $msg = "Invalid META structure. Errors found:\n";
23             $msg .= join( "\n", $pmv->errors );
24             die $msg;
25             }
26              
27             =head1 Description
28              
29             This module validates a PGXN Meta structure against the version of the the
30             specification claimed in the C field of the structure. Currently,
31             there is only v1.0.0.
32              
33             =cut
34              
35             #--------------------------------------------------------------------------#
36             # This code copied and adapted from CPAN::Meta::Valicator by
37             # David Golden and Ricardo Signes ,
38             # which in turn adapted and copied it from Test::CPAN::Meta
39             # by Barbie, for Miss Barbell Productions,
40             # L
41             #--------------------------------------------------------------------------#
42              
43             #--------------------------------------------------------------------------#
44             # Specification Definitions
45             #--------------------------------------------------------------------------#
46              
47             my %known_specs = (
48             '1.0.0' => 'http://pgxn.org/spec/1.0.0/'
49             );
50             my %known_urls = map {$known_specs{$_} => $_} keys %known_specs;
51              
52             my $no_index = {
53             'map' => { file => { list => { value => \&string } },
54             directory => { list => { value => \&string } },
55             ':key' => { name => \&custom, value => \&anything },
56             }
57             };
58              
59             my $prereq_map = {
60             map => {
61             ':key' => {
62             name => \&phase,
63             'map' => {
64             ':key' => {
65             name => \&relation,
66             'map' => { ':key' => { name => \&term, value => \&exversion } }
67             },
68             },
69             }
70             },
71             };
72              
73             my %definitions = (
74             '1.0.0' => {
75             # REQUIRED
76             'abstract' => { mandatory => 1, value => \&string },
77             'maintainer' => { mandatory => 1, list => { value => \&string } },
78             'generated_by' => { mandatory => 0, value => \&string },
79             'license' => {
80             mandatory => 1,
81             listormap => {
82             list => { value => \&license },
83             map => {
84             ':key' => { name => \&anything, value => \&url }
85             },
86             }
87             },
88             'meta-spec' => {
89             mandatory => 1,
90             'map' => {
91             version => { mandatory => 1, value => \&version},
92             url => { value => \&url },
93             ':key' => { name => \&custom, value => \&anything },
94             }
95             },
96             'name' => { mandatory => 1, value => \&term },
97             'release_status' => { mandatory => 0, value => \&release_status },
98             'version' => { mandatory => 1, value => \&version },
99             'provides' => {
100             'mandatory' => 1,
101             'map' => {
102             ':key' => {
103             name => \&term,
104             'map' => {
105             file => { mandatory => 1, value => \&file },
106             version => { mandatory => 1, value => \&version },
107             abstract => { mandatory => 0, value => \&string },
108             docfile => { mandatory => 0, value => \&file },
109             ':key' => { name => \&custom, value => \&anything },
110             }
111             }
112             }
113             },
114              
115             # OPTIONAL
116             'description' => { value => \&string },
117             'tags' => { list => { value => \&tag } },
118             'no_index' => $no_index,
119             'prereqs' => $prereq_map,
120             'resources' => {
121             'map' => {
122             license => { list => { value => \&url } },
123             homepage => { value => \&url },
124             bugtracker => {
125             'map' => {
126             web => { value => \&url },
127             mailto => { value => \&email},
128             ':key' => { name => \&custom, value => \&anything },
129             }
130             },
131             repository => {
132             'map' => {
133             web => { value => \&url },
134             url => { value => \&url },
135             type => { value => \&lc_string },
136             ':key' => { name => \&custom, value => \&anything },
137             }
138             },
139             ':key' => { value => \&string, name => \&custom },
140             }
141             },
142              
143             # CUSTOM -- additional user defined key/value pairs
144             # note we can only validate the key name, as the structure is user defined
145             ':key' => { name => \&custom, value => \&anything },
146             },
147             );
148              
149             #--------------------------------------------------------------------------#
150             # Code
151             #--------------------------------------------------------------------------#
152              
153             =head1 Interface
154              
155             =head2 Constructors
156              
157             =head3 C
158              
159             my $pmv = PGXN::Meta::Validator->new( $struct )
160              
161             The constructor must be passed a metadata structure.
162              
163             =cut
164              
165             sub new {
166 185     185 1 1383 my ($class, $data) = @_;
167              
168             # create an attributes hash
169 185 100 100     1628 my $self = {
170             'data' => $data,
171             'spec' => $data->{'meta-spec'} && $data->{'meta-spec'}{version}
172             ? $data->{'meta-spec'}{version}
173             : '1.0.0',
174             'errors' => undef,
175             };
176              
177             # create the object
178 185         733 return bless $self, $class;
179             }
180              
181             =head3 C
182              
183             my $meta = PGXN::Meta::Validator->load_file('META.json');
184              
185             Reads in the specified JSON file and passes the resulting data structure to
186             C, returning the resulting PGXN::Meta::Validator object. An exception
187             will be thrown if the file does not exist or if its contents are not valid
188             JSON.
189              
190             =cut
191              
192             sub load_file {
193 3     3 1 520 my ($class, $file) = @_;
194              
195 3 100       289 croak "load_file() requires a valid, readable filename"
196             unless -r $file;
197              
198 2         18 $class->new(JSON->new->decode(do {
199 2         7 local $/;
200 2 50       102 open my $fh, '<:raw', $file or croak "Cannot open $file: $!\n";
201 2         686 <$fh>;
202             }));
203             }
204              
205             =head2 Class Method
206              
207             =head3 C
208              
209             say 'PGXN::Meta::Validator ', PGXN::Meta::Validator->version_string;
210              
211             Returns a string representation of the PGXN::Meta::Validator version.
212              
213             =cut
214              
215             sub version_string {
216 0     0 1 0 sprintf 'v%vd', $VERSION;
217             }
218              
219             =head2 Instance Methods
220              
221             =head3 C
222              
223             if ( $pmv->is_valid ) {
224             ...
225             }
226              
227             Returns a boolean value indicating whether the metadata provided
228             is valid.
229              
230             =cut
231              
232             sub is_valid {
233 185     185 1 665 my $self = shift;
234 185         295 my $data = $self->{data};
235 185         296 my $spec_version = $self->{spec};
236 185         531 $self->check_map($definitions{$spec_version}, $data);
237 185         603 return ! $self->errors;
238             }
239              
240             =head3 C
241              
242             warn( join "\n", $pmv->errors );
243              
244             Returns a list of errors seen during validation.
245              
246             =cut
247              
248             sub errors {
249 261     261 1 341 my $self = shift;
250 261 100       5049 return () unless defined $self->{errors};
251 152         175 return @{$self->{errors}};
  152         963  
252             }
253              
254             =begin internals
255              
256             =head2 Check Methods
257              
258             =over
259              
260             =item * check_map($spec,$data)
261              
262             Checks whether a map (or hash) part of the data structure conforms to the
263             appropriate specification definition.
264              
265             =item * check_list($spec,$data)
266              
267             Checks whether a list conforms, but converts strings to a single-element list
268              
269             =item * check_listormap($spec,$data)
270              
271             Checks whether a map or lazy list conforms to the appropriate specification
272             definition.
273              
274             =back
275              
276             =cut
277              
278             my $spec_error = "Missing validation action in specification. "
279             . "Must be one of 'map', 'list', or 'value'";
280              
281             sub check_map {
282 2198     2198 1 3262 my ($self,$spec,$data) = @_;
283              
284 2198 100       5297 if(ref($spec) ne 'HASH') {
285 1         5 $self->_error( "Unknown META specification, cannot validate." );
286 1         3 return;
287             }
288              
289 2197 100       3908 if(ref($data) ne 'HASH') {
290 6         17 $self->_error( 'Should be a map structure' );
291 6         8 return;
292             }
293              
294 2191         4748 for my $key (keys %$spec) {
295 7516 100       18878 next unless($spec->{$key}->{mandatory});
296 1835 100       3902 next if(defined $data->{$key});
297 12         16 push @{$self->{stack}}, $key;
  12         32  
298 12         33 $self->_error( 'missing', 'Required field' );
299 12         14 pop @{$self->{stack}};
  12         25  
300             }
301              
302 2191         5903 for my $key (keys %$data) {
303 5856         8778 push @{$self->{stack}}, $key;
  5856         11738  
304 5856 100       12328 if($spec->{$key}) {
    50          
305 4384 100       10152 if($spec->{$key}{value}) {
    100          
    100          
    50          
306 2712         6437 $spec->{$key}{value}->($self,$key,$data->{$key});
307             } elsif($spec->{$key}{'map'}) {
308 1106         2993 $self->check_map($spec->{$key}{'map'},$data->{$key});
309             } elsif($spec->{$key}{'list'}) {
310 383         1017 $self->check_list($spec->{$key}{'list'},$data->{$key});
311             } elsif($spec->{$key}{'listormap'}) {
312 183         529 $self->check_listormap($spec->{$key}{'listormap'},$data->{$key});
313             } else {
314 0         0 $self->_error( "$spec_error for '$key'" );
315             }
316              
317             } elsif ($spec->{':key'}) {
318 1472         3316 $spec->{':key'}{name}->($self,$key,$key);
319 1472 100       4474 if($spec->{':key'}{value}) {
    50          
    0          
    0          
320 717         1614 $spec->{':key'}{value}->($self,$key,$data->{$key});
321             } elsif($spec->{':key'}{'map'}) {
322 755         2050 $self->check_map($spec->{':key'}{'map'},$data->{$key});
323             } elsif($spec->{':key'}{'list'}) {
324 0         0 $self->check_list($spec->{':key'}{'list'},$data->{$key});
325             } elsif($spec->{':key'}{'listormap'}) {
326 0         0 $self->check_listormap($spec->{':key'}{'listormap'},$data->{$key});
327             } else {
328 0         0 $self->_error( "$spec_error for ':key'" );
329             }
330              
331              
332             } else {
333 0         0 $self->_error( "Unknown key, '$key', found in map structure" );
334             }
335 5856         32651 pop @{$self->{stack}};
  5856         15336  
336             }
337             }
338              
339             # if it's a string, make it into a list and check the list
340             sub check_list {
341 414     414 1 634 my ($self,$spec,$data) = @_;
342              
343 414 100 100     1792 if ( defined $data && ! ref $data) {
344 38         79 $self->_list_value($spec, $data);
345             } else {
346 376 100       867 $self->_error( 'Should be a list structure' ) if ref $data ne 'ARRAY';
347 376 50 33     904 $self->_error( "Missing entries from required list" )
348             if defined $spec->{mandatory} && !defined $data->[0];
349 376         374 my $field = pop @{ $self->{stack} };
  376         685  
350 376         596 my $i = 0;
351 376         391 for my $value (@{ $data }) {
  376         704  
352 1265         1375 push @{$self->{stack}}, $field . "[$i]";
  1265         3431  
353 1265         1397 $i++;
354 1265         3303 $self->_list_value($spec, $value);
355 1265         1792 pop @{$self->{stack}};
  1265         3041  
356             }
357 376         542 push @{$self->{stack}}, $field;
  376         1118  
358             }
359             }
360              
361             sub _list_value {
362 1303     1303   1915 my ($self, $spec, $value) = @_;
363 1303 50       2400 if(defined $spec->{value}) {
    0          
    0          
    0          
    0          
    0          
364 1303         2376 $spec->{value}->($self,'list',$value);
365             } elsif(defined $spec->{'map'}) {
366 0         0 $self->check_map($spec->{'map'},$value);
367             } elsif(defined $spec->{'list'}) {
368 0         0 $self->check_list($spec->{'list'},$value);
369             } elsif(defined $spec->{'list'}) {
370 0         0 $self->check_list($spec->{'list'},$value);
371             } elsif(defined $spec->{'listormap'}) {
372 0         0 $self->check_listormap($spec->{'listormap'},$value);
373             } elsif ($spec->{':key'}) {
374 0         0 $self->check_map($spec,$value);
375             } else {
376 0         0 $self->_error( "$spec_error associated with '$self->{stack}[-2]'" );
377             }
378             }
379              
380             sub check_listormap {
381 183     183 1 274 my ($self,$spec,$data) = @_;
382              
383 183 100       639 return ref $data eq 'HASH'
384             ? $self->check_map($spec->{map}, $data)
385             : $self->check_list($spec->{list}, $data);
386             }
387              
388             =head2 Validator Methods
389              
390             =over
391              
392             =item * anything($self,$key,$value)
393              
394             Any value is valid, so this function always returns true.
395              
396             =item * url($self,$key,$value)
397              
398             Validates that a given value is in an acceptable URL format
399              
400             =item * email($self,$key,$value)
401              
402             Validates that a given value is in an acceptable EMAIL format
403              
404             =item * urlspec($self,$key,$value)
405              
406             Validates that the URL to a META specification is a known one.
407              
408             =item * string_or_undef($self,$key,$value)
409              
410             Validates that the value is either a string or an undef value. Bit of a
411             catchall function for parts of the data structure that are completely user
412             defined.
413              
414             =item * string($self,$key,$value)
415              
416             Validates that a string exists for the given key.
417              
418             =item * lc_string($self,$key,$value)
419              
420             Validates that a string exists for the given key and contains only lowercase
421             characters.
422              
423             =item * file($self,$key,$value)
424              
425             Validate that a file is passed for the given key. This may be made more
426             thorough in the future. For now it acts like \&string.
427              
428             =item * phase($self,$key,$value)
429              
430             Validate that a prereq phase is one of "configure", "build", "test",
431             "runtime", or "develop".
432              
433             =item * relation($self,$key,$value)
434              
435             Validate that a prereq relation is one of "requires", "recommends",
436             "suggests", or "conflicts".
437              
438             =item * release_status($self,$key,$value)
439              
440             Validate that release status is one of "stable", "testing", or "unstable".
441              
442             =item * exversion($self,$key,$value)
443              
444             Validates a list of versions, e.g. '<= 5, >=2, ==3, !=4, >1, <6, 0'.
445              
446             =item * version($self,$key,$value)
447              
448             Validates a single version string. Versions of the type '5.8.8' and '0.00_00'
449             are both valid. A leading 'v' like 'v1.2.3' is also valid.
450              
451             =item * boolean($self,$key,$value)
452              
453             Validates for a boolean value. Currently these values are '1', '0', 'true',
454             'false', however the latter 2 may be removed.
455              
456             =item * license($self,$key,$value)
457              
458             Validates that a value is given for the license. Returns 1 if an known license
459             type, or 2 if a value is given but the license type is not a recommended one.
460              
461             =item * custom($self,$key,$value)
462              
463             Validates that the given key begins with 'x_' or 'X_', to indicate a user
464             defined tag and only has characters in the class [-_a-zA-Z]
465              
466             =item * identifier($self,$key,$value)
467              
468             Validates that key is in an acceptable format for the META specification,
469             for an identifier, i.e. any that matches the regular expression
470             qr/[a-z][a-z_]/i.
471              
472             =item * term($self,$key,$value)
473              
474             Validates that a given value is in an acceptable term, i.e., at least two
475             characters and does not contain control characters, spaces, C, or C<\>.
476              
477             =item * tag($self,$key,$value)
478              
479             Validates that a given value is in an acceptable tag, i.e., between 1 and 256
480             characters and does not contain control characters, C, or C<\>.
481              
482             =back
483              
484             =end internals
485              
486             =cut
487              
488             sub release_status {
489 5     5 1 8 my ($self,$key,$value) = @_;
490 5 100       14 $value = '' unless defined $value;
491 5 100       27 return 1 if $value =~ /\A(?:(?:un)?stable|testing)\z/;
492 2         6 $self->_error( qq{"$value" is not a valid release status; must be one of stable, testing, unstable} );
493 2         4 return 0;
494             }
495              
496             # _uri_split taken from URI::Split by Gisle Aas, Copyright 2003
497             sub _uri_split {
498 1039     1039   6796 return $_[0] =~ m,(?:([^:/?#]+):)?(?://([^/?#]*))?([^?#]*)(?:\?([^#]*))?(?:#(.*))?,;
499             }
500              
501             sub url {
502 1040     1040 1 1529 my ($self,$key,$value) = @_;
503 1040 100 66     4074 if (defined $value && $value ne '') {
504             # XXX Consider using Data::Validate::URI.
505 1039         1778 my ($scheme, $auth, $path, $query, $frag) = _uri_split($value);
506 1039 100 66     4539 unless ( defined $scheme && length $scheme ) {
507 7         27 $self->_error( qq{"$value" is not a valid URL} );
508 7         15 return 0;
509             }
510 1032 50 33     3983 unless ( defined $auth && length $auth ) {
511 0         0 $self->_error( qq{"$value" is not a valid URL} );
512 0         0 return 0;
513             }
514 1032         2185 return 1;
515             } else {
516 1         3 $self->_error('No value');
517 1         2 return 0;
518             }
519 0         0 $self->_error( qq{"$value" is not a valid URL} );
520 0         0 return 0;
521             }
522              
523             sub urlspec {
524 0     0 1 0 my ($self,$key,$value) = @_;
525 0 0       0 if(defined $value) {
526 0 0 0     0 return 1 if($value && $known_specs{$self->{spec}} eq $value);
527 0 0 0     0 if($value && $known_urls{$value}) {
528 0         0 $self->_error( 'META specification URL does not match version' );
529 0         0 return 0;
530             }
531             }
532 0         0 $self->_error( 'Unknown META specification' );
533 0         0 return 0;
534             }
535              
536             sub email {
537 3     3 1 7 my ($self, $key, $value) = @_;
538             # XXX Consider using Email::Valid.
539 3 100 66     33 return 1 if defined $value && $value =~ /@/;
540 1         4 $self->_error( qq{"$value" is not a valid email address} );
541 1         2 return 0;
542             }
543              
544 164     164 1 204 sub anything { return 1 }
545              
546             sub string {
547 1284     1284 1 1996 my ($self,$key,$value) = @_;
548 1284 100       2793 if(defined $value) {
549 1279 100 66     4002 return 1 if($value || $value =~ /^0$/);
550             }
551 9         26 $self->_error( "No value" );
552 9         19 return 0;
553             }
554              
555             sub lc_string {
556 175     175 1 271 my ($self, $key, $value) = @_;
557 175 50       388 $self->string($key, $value) or return 0;
558 2 100   2   4004 return 1 if $value !~ /\p{Upper}/;
  2         26  
  2         30  
  175         636  
559 1         4 $self->_error( qq{"$value" is not a lowercase string} );
560 1         2 return 0;
561             }
562              
563             sub term {
564 918     918 1 4362 shift->_string_class(@_, term => qr{[[:space:][:cntrl:]/\\]}, 2);
565             }
566              
567             sub tag {
568 892     892 1 3945 shift->_string_class(@_, tag => qr{[[:cntrl:]/\\]}, 1, 256);
569             }
570              
571             sub _string_class {
572 1810     1810   4289 my ($self, $key, $value, $type, $regex, $min, $max) = @_;
573 1810 100       3609 unless (defined $value) {
574 1         5 $self->_error( 'value is not defined' );
575 1         3 return 0;
576             }
577              
578 1809 100 66     12678 if (($value || $value =~ /^0$/) && $value !~ $regex) {
      100        
579 1801 100 66     7066 if ($min && length $value < $min) {
580 1         6 $self->_error("$type must be at least $min characters");
581 1         3 return 0;
582             }
583 1800 100 100     5264 if ($max && length $value > $max) {
584 1         7 $self->_error("$type must be no more than $max characters");
585 1         3 return 0;
586             }
587 1799         3874 return 1;
588             } else {
589 8         30 $self->_error(qq{"$value" is not a valid $type});
590 8         20 return 0;
591             }
592             }
593              
594             sub string_or_undef {
595 0     0 1 0 my ($self,$key,$value) = @_;
596 0 0       0 return 1 unless(defined $value);
597 0 0 0     0 return 1 if($value || $value =~ /^0$/);
598 0         0 $self->_error( "No string defined for '$key'" );
599 0         0 return 0;
600             }
601              
602             sub file {
603 183     183 1 300 my ($self,$key,$value) = @_;
604 183 100       570 return 1 if(defined $value);
605 2         7 $self->_error( 'No value' );
606 2         4 return 0;
607             }
608              
609             sub exversion {
610 553     553 1 891 my ($self,$key,$value) = @_;
611 553 50 66     2369 if (defined $value && ($value || $value eq '0')) {
      66        
612 552         621 my $pass = 1;
613 552         1464 for my $val (split /,\s*/, $value) {
614 557 100       1540 if ($val ne '') {
615 556 100       1306 next if $val eq '0';
616 380 100       1258 if ($val =~ s/^([^\d\s]+)\s*//) {
617 20         48 my $op = $1;
618 20 100       90 if ($op !~ /^[<>](?:=)?|==|!=$/) {
619 2         16 $self->_error(qq{"$op" is not a valid version range operator});
620 2         4 $pass = 0;
621             }
622             }
623 380 100       507 unless (eval { SemVer->new($val) }) {
  380         1224  
624             # Field /version: Value "0.01.2" is not a valid version [Spec v1.0.0]
625 2         403 $self->_error( qq{"$val" is not a valid semantic version} );
626 2         6 $pass = 0;
627             }
628             } else {
629 1         5 $self->_error( qq{"" is not a valid semantic version} );
630 1         2 $pass = 0;
631             }
632             }
633 552         18488 return $pass;
634             }
635 1 50       19 $self->_error($value ? qq{value "$value" is not a valid semantic version} : 'No value' );
636 1         1 return 0;
637             }
638              
639             sub version {
640 546     546 1 953 my ($self,$key,$value) = @_;
641 546 100       1029 if (defined $value) {
642 545 100       702 return 1 if eval { SemVer->new($value) };
  545         1601  
643             } else {
644 1         3 $value = '';
645             }
646 7         1383 $self->_error( qq{"$value" is not a valid semantic version} );
647 7         15 return 0;
648             }
649              
650             sub boolean {
651 0     0 1 0 my ($self,$key,$value) = @_;
652 0 0       0 if(defined $value) {
653 0 0       0 return 1 if($value =~ /^(0|1|true|false)$/);
654             } else {
655 0         0 $value = '';
656             }
657 0         0 $self->_error( "'$value' for '$key' is not a boolean value." );
658 0         0 return 0;
659             }
660              
661             my %licenses = map { $_ => 1 } qw(
662             agpl_3
663             apache_1_1
664             apache_2_0
665             artistic_1
666             artistic_2
667             bsd
668             freebsd
669             gfdl_1_2
670             gfdl_1_3
671             gpl_1
672             gpl_2
673             gpl_3
674             lgpl_2_1
675             lgpl_3_0
676             mit
677             mozilla_1_0
678             mozilla_1_1
679             openssl
680             perl_5
681             postgresql
682             qpl_1_0
683             ssleay
684             sun
685             zlib
686             open_source
687             restricted
688             unrestricted
689             unknown
690             );
691              
692             sub license {
693 33     33 1 45 my ($self,$key,$value) = @_;
694 33 50       59 if(defined $value) {
695 33 100 66     198 return 1 if $value && exists $licenses{$value};
696             } else {
697 0         0 $value = '';
698             }
699 2         10 $self->_error( qq{"$value" is an unknown license} );
700 2         6 return 0;
701             }
702              
703             sub custom {
704 10     10 1 28 my ($self,$key) = @_;
705 10 50       24 if(defined $key) {
706 10 100 66     82 return 1 if($key && $key =~ /^x_/i); # user defined
707             } else {
708 0         0 $key = '';
709             }
710 5         15 $self->_error( 'Unknown key; custom keys must begin with "x_" or "X_"' );
711 5         8 return 0;
712             }
713              
714             sub identifier {
715 0     0 1 0 my ($self,$key) = @_;
716 0 0       0 if(defined $key) {
717 0 0 0     0 return 1 if($key && $key =~ /^([a-z][_a-z]+)$/i); # spec 2.0 defined
718             } else {
719 0         0 $key = '';
720             }
721 0         0 $self->_error( "Key '$key' is not a legal identifier." );
722 0         0 return 0;
723             }
724              
725             my @valid_phases = qw/ configure build test runtime develop /;
726             sub phase {
727 196     196 1 341 my ($self,$key) = @_;
728 196 50       396 if(defined $key) {
729 196 100 66     575 return 1 if( length $key && grep { $key eq $_ } @valid_phases );
  980         2348  
730 1 50       8 return 1 if $key =~ /x_/i;
731             }
732 1         9 $self->_error('Unknown preqreq phase; must be one of ' . join ', ', @valid_phases);
733 1         2 return 0;
734             }
735              
736             my @valid_relations = qw/ requires recommends suggests conflicts/;
737             sub relation {
738 377     377 1 532 my ($self,$key) = @_;
739 377 50       758 if(defined $key) {
740 377 100 66     953 return 1 if( length $key && grep { $key eq $_ } @valid_relations );
  1508         3718  
741 1 50       4 return 1 if $key =~ /x_/i;
742             }
743 1         6 $self->_error('Unknown preqreq relationship; must be one of ' . join ', ', @valid_relations);
744 1         1 return 0;
745             }
746              
747             sub _error {
748 78     78   118 my $self = shift;
749 78         110 my $mess = shift;
750 78   100     289 my $label = shift || 'Field';
751              
752 78 100       253 $mess = "$label /" . join( '/' => @{ $self->{stack} }) . ": $mess" if $self->{stack};
  77         262  
753 78         180 $mess .= " [Spec v$self->{spec}]";
754              
755 78         85 push @{$self->{errors}}, $mess;
  78         214  
756             }
757              
758             1;
759              
760             __END__