1 package Parse::CPAN::Meta;
7 sub HAVE_UTF8 () { $] >= 5.007003 }
10 # The string eval helps hide this from Test::MinimumVersion
12 die "Failed to load UTF-8 support" if $@;
18 $Parse::CPAN::Meta::VERSION = '1.39';
19 @Parse::CPAN::Meta::ISA = qw{ Exporter };
20 @Parse::CPAN::Meta::EXPORT_OK = qw{ Load LoadFile };
30 # Printable characters for escapes
32 z => "\x00", a => "\x07", t => "\x09",
33 n => "\x0a", v => "\x0b", f => "\x0c",
34 r => "\x0d", e => "\x1b", '\\' => '\\',
41 #####################################################################
44 # Create an object from a file
48 croak('You did not specify a file name') unless $file;
49 croak( "File '$file' does not exist" ) unless -e $file;
50 croak( "'$file' is a directory, not a file" ) unless -f _;
51 croak( "Insufficient permissions to read '$file'" ) unless -r _;
56 unless ( open( CFG, $file ) ) {
57 croak("Failed to open file '$file': $!");
60 unless ( close(CFG) ) {
61 croak("Failed to close file '$file': $!");
64 # Hand off to the actual parser
68 # Parse a document from a string.
69 # Doing checks on $_[0] prevents us having to do a string copy.
72 unless ( defined $string ) {
73 croak("Did not provide a string to load");
77 if ( $string =~ /^(?:\376\377|\377\376|\377\376\0\0|\0\0\376\377)/ ) {
78 croak("Stream has a non UTF-8 Unicode Byte Order Mark");
80 # Strip UTF-8 bom if found, we'll just ignore it
81 $string =~ s/^\357\273\277//;
84 # Try to decode as utf8
85 utf8::decode($string) if HAVE_UTF8;
87 # Check for some special cases
88 return () unless length $string;
89 unless ( $string =~ /[\012\015]+\z/ ) {
90 croak("Stream does not end with newline character");
93 # Split the file into lines
94 my @lines = grep { ! /^\s*(?:\#.*)?\z/ }
95 split /(?:\015{1,2}\012|\015|\012)/, $string;
97 # Strip the initial YAML header
98 @lines and $lines[0] =~ /^\%YAML[: ][\d\.]+.*\z/ and shift @lines;
103 # Do we have a document header?
104 if ( $lines[0] =~ /^---\s*(?:(.+)\s*)?\z/ ) {
105 # Handle scalar documents
107 if ( defined $1 and $1 !~ /^(?:\#.+|\%YAML[: ][\d\.]+)\z/ ) {
108 push @documents, _scalar( "$1", [ undef ], \@lines );
113 if ( ! @lines or $lines[0] =~ /^(?:---|\.\.\.)/ ) {
115 push @documents, undef;
116 while ( @lines and $lines[0] !~ /^---/ ) {
120 } elsif ( $lines[0] =~ /^\s*\-/ ) {
121 # An array at the root
123 push @documents, $document;
124 _array( $document, [ 0 ], \@lines );
126 } elsif ( $lines[0] =~ /^(\s*)\S/ ) {
129 push @documents, $document;
130 _hash( $document, [ length($1) ], \@lines );
133 croak("Parse::CPAN::Meta failed to classify line '$lines[0]'");
140 return $documents[-1];
144 # Deparse a scalar string to the actual scalar
146 my ($string, $indent, $lines) = @_;
148 # Trim trailing whitespace
149 $string =~ s/\s*\z//;
151 # Explitic null/undef
152 return undef if $string eq '~';
155 if ( $string =~ /^\'(.*?)\'\z/ ) {
156 return '' unless defined $1;
158 $string =~ s/\'\'/\'/g;
161 if ( $string =~ /^\"((?:\\.|[^\"])*)\"\z/ ) {
162 # Reusing the variable is a little ugly,
163 # but avoids a new variable and a string copy.
165 $string =~ s/\\"/"/g;
166 $string =~ s/\\([never\\fartz]|x([0-9a-fA-F]{2}))/(length($1)>1)?pack("H2",$2):$UNESCAPES{$1}/gex;
171 if ( $string =~ /^[\'\"!&]/ ) {
172 croak("Parse::CPAN::Meta does not support a feature in line '$lines->[0]'");
174 return {} if $string eq '{}';
175 return [] if $string eq '[]';
177 # Regular unquoted string
178 return $string unless $string =~ /^[>|]/;
181 croak("Parse::CPAN::Meta failed to find multi-line scalar content") unless @$lines;
183 # Check the indent depth
184 $lines->[0] =~ /^(\s*)/;
185 $indent->[-1] = length("$1");
186 if ( defined $indent->[-2] and $indent->[-1] <= $indent->[-2] ) {
187 croak("Parse::CPAN::Meta found bad indenting in line '$lines->[0]'");
193 $lines->[0] =~ /^(\s*)/;
194 last unless length($1) >= $indent->[-1];
195 push @multiline, substr(shift(@$lines), length($1));
198 my $j = (substr($string, 0, 1) eq '>') ? ' ' : "\n";
199 my $t = (substr($string, 1, 1) eq '-') ? '' : "\n";
200 return join( $j, @multiline ) . $t;
205 my ($array, $indent, $lines) = @_;
208 # Check for a new document
209 if ( $lines->[0] =~ /^(?:---|\.\.\.)/ ) {
210 while ( @$lines and $lines->[0] !~ /^---/ ) {
216 # Check the indent level
217 $lines->[0] =~ /^(\s*)/;
218 if ( length($1) < $indent->[-1] ) {
220 } elsif ( length($1) > $indent->[-1] ) {
221 croak("Parse::CPAN::Meta found bad indenting in line '$lines->[0]'");
224 if ( $lines->[0] =~ /^(\s*\-\s+)[^\'\"]\S*\s*:(?:\s+|$)/ ) {
226 my $indent2 = length("$1");
227 $lines->[0] =~ s/-/ /;
229 _hash( $array->[-1], [ @$indent, $indent2 ], $lines );
231 } elsif ( $lines->[0] =~ /^\s*\-(\s*)(.+?)\s*\z/ ) {
232 # Array entry with a value
234 push @$array, _scalar( "$2", [ @$indent, undef ], $lines );
236 } elsif ( $lines->[0] =~ /^\s*\-\s*\z/ ) {
242 if ( $lines->[0] =~ /^(\s*)\-/ ) {
243 my $indent2 = length("$1");
244 if ( $indent->[-1] == $indent2 ) {
250 _array( $array->[-1], [ @$indent, $indent2 ], $lines );
253 } elsif ( $lines->[0] =~ /^(\s*)\S/ ) {
255 _hash( $array->[-1], [ @$indent, length("$1") ], $lines );
258 croak("Parse::CPAN::Meta failed to classify line '$lines->[0]'");
261 } elsif ( defined $indent->[-2] and $indent->[-1] == $indent->[-2] ) {
262 # This is probably a structure like the following...
268 # ... so lets return and let the hash parser handle it
272 croak("Parse::CPAN::Meta failed to classify line '$lines->[0]'");
281 my ($hash, $indent, $lines) = @_;
284 # Check for a new document
285 if ( $lines->[0] =~ /^(?:---|\.\.\.)/ ) {
286 while ( @$lines and $lines->[0] !~ /^---/ ) {
292 # Check the indent level
293 $lines->[0] =~ /^(\s*)/;
294 if ( length($1) < $indent->[-1] ) {
296 } elsif ( length($1) > $indent->[-1] ) {
297 croak("Parse::CPAN::Meta found bad indenting in line '$lines->[0]'");
301 unless ( $lines->[0] =~ s/^\s*([^\'\" ][^\n]*?)\s*:(\s+|$)// ) {
302 if ( $lines->[0] =~ /^\s*[?\'\"]/ ) {
303 croak("Parse::CPAN::Meta does not support a feature in line '$lines->[0]'");
305 croak("Parse::CPAN::Meta failed to classify line '$lines->[0]'");
309 # Do we have a value?
310 if ( length $lines->[0] ) {
312 $hash->{$key} = _scalar( shift(@$lines), [ @$indent, undef ], $lines );
317 $hash->{$key} = undef;
320 if ( $lines->[0] =~ /^(\s*)-/ ) {
322 _array( $hash->{$key}, [ @$indent, length($1) ], $lines );
323 } elsif ( $lines->[0] =~ /^(\s*)./ ) {
324 my $indent2 = length("$1");
325 if ( $indent->[-1] >= $indent2 ) {
327 $hash->{$key} = undef;
330 _hash( $hash->{$key}, [ @$indent, length($1) ], $lines );
347 Parse::CPAN::Meta - Parse META.yml and other similar CPAN metadata files
351 #############################################
364 #############################################
367 use Parse::CPAN::Meta;
370 my @yaml = Parse::CPAN::Meta::LoadFile( 'Meta.yml' );
373 my $root = $yaml[0]->{rootproperty};
374 my $one = $yaml[0]->{section}->{one};
375 my $Foo = $yaml[0]->{section}->{Foo};
379 B<Parse::CPAN::Meta> is a parser for F<META.yml> files, based on the
380 parser half of L<YAML::Tiny>.
382 It supports a basic subset of the full YAML specification, enough to
383 implement parsing of typical F<META.yml> files, and other similarly simple
386 If you need something with more power, move up to a full YAML parser such
387 as L<YAML>, L<YAML::Syck> or L<YAML::LibYAML>.
389 B<Parse::CPAN::Meta> provides a very simply API of only two functions,
390 based on the YAML functions of the same name. Wherever possible,
391 identical calling semantics are used.
393 All error reporting is done with exceptions (die'ing).
397 For maintenance clarity, no functions are exported.
401 my @yaml = Load( $string );
403 Parses a string containing a valid YAML stream into a list of Perl data
408 my @yaml = LoadFile( 'META.yml' );
410 Reads the YAML stream from a file instead of a string.
414 Bugs should be reported via the CPAN bug tracker at
416 L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Parse-CPAN-Meta>
420 Adam Kennedy E<lt>adamk@cpan.orgE<gt>
424 L<YAML>, L<YAML::Syck>, L<Config::Tiny>, L<CSS::Tiny>,
425 L<http://use.perl.org/~Alias/journal/29427>, L<http://ali.as/>
429 Copyright 2006 - 2009 Adam Kennedy.
431 This program is free software; you can redistribute
432 it and/or modify it under the same terms as Perl itself.
434 The full text of the license can be found in the
435 LICENSE file included with this module.