File Coverage

blib/lib/R/DescriptionFile.pm
Criterion Covered Total %
statement 77 77 100.0
branch 18 20 90.0
condition 7 9 77.7
subroutine 13 13 100.0
pod 0 4 0.0
total 115 123 93.5


line stmt bran cond sub pod time code
1             package R::DescriptionFile;
2              
3             # ABSTRACT: R package DESCRIPTION file parser
4              
5 1     1   23571 use strict;
  1         10  
  1         29  
6 1     1   11 use warnings;
  1         2  
  1         40  
7              
8 1     1   6 use Path::Tiny;
  1         2  
  1         1101  
9              
10             our $VERSION = '0.004'; # VERSION
11              
12             my @keys_deps = qw(Depends Imports Suggests LinkingTo Enhances);
13             my @keys_list_type = qw(
14             URL Additional_repositories VignetteBuilder
15             );
16             my @keys_logical = qw(
17             LazyData LazyLoad KeepSource ByteCompile ZipData Biarch BuildVignettes
18             NeedsCompilation
19             );
20              
21             sub new {
22 5     5 0 9 my $class = shift;
23 5         11 my $self = bless {}, $class;
24 5         11 return $self;
25             }
26              
27             sub parse_file {
28 4     4 0 10005 my ( $proto, $file ) = @_;
29 4 50       20 my $self = ref $proto ? $proto : $proto->new;
30 4         11 my @lines = path($file)->lines_utf8( { chomp => 1 } );
31 4         2868 return $self->_parse_lines( \@lines );
32             }
33              
34             sub parse_text {
35 1     1 0 2515 my ( $proto, $text ) = @_;
36 1 50       6 my $self = ref $proto ? $proto : $proto->new;
37 1         25 my @lines = split( /\n+/, $text );
38 1         5 return $self->_parse_lines( \@lines );
39             }
40              
41             sub _parse_lines {
42 5     5   29 my ( $self, $lines ) = @_;
43              
44 5         11 my $line_idx = 0;
45              
46             my $get_line = sub {
47 151     151   318 my $line = $lines->[ $line_idx++ ];
48 151   100     979 while ( defined $line and $line =~ /^\s*$/ ) {
49 2         11 $line = $lines->[ $line_idx++ ];
50             }
51 151         324 return $line;
52 5         25 };
53              
54 5         13 my $curr_line = &$get_line();
55 5         13 while ( defined $curr_line ) {
56 146         230 my $next_line = &$get_line();
57 146 100 100     581 if ( defined $next_line and $next_line =~ /^\s+(.+)/ ) {
58 71         197 $curr_line .= ' ' . $1;
59 71         140 next;
60             }
61              
62 75         217 $self->_parse_line( $curr_line, $line_idx );
63 74         199 $curr_line = $next_line;
64             }
65              
66 4         20 $self->_check_mandatory_fields;
67              
68 3         22 return $self;
69             }
70              
71             sub _parse_line {
72 75     75   248 my ( $self, $line, $line_idx ) = @_;
73              
74 75         288 my ( $key, $val ) = split( /:/, $line, 2 );
75 75 100       160 unless ( defined $val ) {
76 1         18 die "Invalid DESCRIPTION. Field not seen at line $line_idx: $line";
77             }
78              
79 74         160 $key = _trim($key);
80 74         131 $val = _trim($val);
81              
82 74 100       120 if ( grep { $key eq $_ } @keys_deps ) {
  370 100       767  
    100          
83 12         24 my $deps = _split_list($val);
84             my %deps_hash = map {
85 12         26 $_ =~ /([^\(]*)(?:\((.*)\))?/;
  58         191  
86 58 100       94 my ( $pkg, $req ) = map { defined $_ ? _trim($_) : '' } ( $1, $2 );
  116         318  
87 58         176 ( $pkg => $req );
88             } @$deps;
89 12         52 $self->{$key} = \%deps_hash;
90             }
91 186         374 elsif ( grep { $key eq $_ } @keys_list_type ) {
92 6         21 $self->{$key} = _split_list($val);
93             }
94 448         796 elsif ( grep { $key eq $_ } @keys_logical ) {
95 4         29 $self->{$key} = !!( $val =~ /^(yes|true)$/ );
96             }
97             else {
98 52         158 $self->{$key} = $val;
99             }
100             }
101              
102             sub _check_mandatory_fields {
103 4     4   9 my ($self) = @_;
104              
105 4         9 my @missing_fields = grep { !exists $self->{$_} } qw(
  20         56  
106             Package Version License Description Title
107             );
108 4 100       14 if ( !exists $self->{'Authors@R'} ) {
109             push @missing_fields,
110 2         4 grep { !exists $self->{$_} } qw(Author Maintainer);
  4         11  
111             }
112 4         12 @missing_fields = sort @missing_fields;
113              
114 4 100       10 if (@missing_fields) {
115 1         30 die "Invalid DESRIPTION. Missing mandatory fields: "
116             . join( ", ", @missing_fields );
117             }
118             }
119              
120             sub get {
121 5     5 0 1103 my ( $self, $key ) = @_;
122 5         30 return $self->{$key};
123             }
124              
125             ## utlities
126              
127             sub _trim {
128 330     330   629 my ($s) = @_;
129 330         962 $s =~ s/^\s+//s;
130 330         879 $s =~ s/\s+$//s;
131 330         838 return $s;
132             }
133              
134             sub _split_list {
135 18     18   53 my ( $s, $r_sep ) = @_;
136 18   33     110 $r_sep ||= qr/,/;
137 18         67 my @lst = map { _trim($_) } split( $r_sep, _trim($s) );
  69         263  
138 18         118 return \@lst;
139             }
140              
141             1;
142              
143             __END__