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   23473 use strict;
  1         2  
  1         27  
6 1     1   5 use warnings;
  1         1  
  1         23  
7              
8 1     1   4 use Path::Tiny;
  1         2  
  1         939  
9              
10             our $VERSION = '0.005'; # 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 6 my $class = shift;
23 5         8 my $self = bless {}, $class;
24 5         11 return $self;
25             }
26              
27             sub parse_file {
28 4     4 0 8288 my ( $proto, $file ) = @_;
29 4 50       13 my $self = ref $proto ? $proto : $proto->new;
30 4         8 my @lines = path($file)->lines_utf8( { chomp => 1 } );
31 4         2305 return $self->_parse_lines( \@lines );
32             }
33              
34             sub parse_text {
35 1     1 0 1813 my ( $proto, $text ) = @_;
36 1 50       5 my $self = ref $proto ? $proto : $proto->new;
37 1         19 my @lines = split( /\n+/, $text );
38 1         4 return $self->_parse_lines( \@lines );
39             }
40              
41             sub _parse_lines {
42 5     5   11 my ( $self, $lines ) = @_;
43              
44 5         6 my $line_idx = 0;
45              
46             my $get_line = sub {
47 151     151   188 my $line = $lines->[ $line_idx++ ];
48 151   100     454 while ( defined $line and $line =~ /^\s*$/ ) {
49 2         7 $line = $lines->[ $line_idx++ ];
50             }
51 151         222 return $line;
52 5         20 };
53              
54 5         8 my $curr_line = &$get_line();
55 5         9 while ( defined $curr_line ) {
56 146         179 my $next_line = &$get_line();
57 146 100 100     388 if ( defined $next_line and $next_line =~ /^\s+(.+)/ ) {
58 71         130 $curr_line .= ' ' . $1;
59 71         111 next;
60             }
61              
62 75         142 $self->_parse_line( $curr_line, $line_idx );
63 74         144 $curr_line = $next_line;
64             }
65              
66 4         9 $self->_check_mandatory_fields;
67              
68 3         15 return $self;
69             }
70              
71             sub _parse_line {
72 75     75   102 my ( $self, $line, $line_idx ) = @_;
73              
74 75         203 my ( $key, $val ) = split( /:/, $line, 2 );
75 75 100       126 unless ( defined $val ) {
76 1         13 die "Invalid DESCRIPTION. Field not seen at line $line_idx: $line";
77             }
78              
79 74         89 $key = _trim($key);
80 74         115 $val = _trim($val);
81              
82 74 100       105 if ( grep { $key eq $_ } @keys_deps ) {
  370 100       555  
    100          
83 12         16 my $deps = _split_list($val);
84             my %deps_hash = map {
85 12         20 $_ =~ /([^\(]*)(?:\((.*)\))?/;
  58         128  
86 58 100       71 my ( $pkg, $req ) = map { defined $_ ? _trim($_) : '' } ( $1, $2 );
  116         209  
87 58         131 ( $pkg => $req );
88             } @$deps;
89 12         48 $self->{$key} = \%deps_hash;
90             }
91 186         261 elsif ( grep { $key eq $_ } @keys_list_type ) {
92 6         8 $self->{$key} = _split_list($val);
93             }
94 448         566 elsif ( grep { $key eq $_ } @keys_logical ) {
95 4         20 $self->{$key} = !!( $val =~ /^(yes|true)$/ );
96             }
97             else {
98 52         126 $self->{$key} = $val;
99             }
100             }
101              
102             sub _check_mandatory_fields {
103 4     4   5 my ($self) = @_;
104              
105 4         6 my @missing_fields = grep { !exists $self->{$_} } qw(
  20         34  
106             Package Version License Description Title
107             );
108 4 100       9 if ( !exists $self->{'Authors@R'} ) {
109             push @missing_fields,
110 2         3 grep { !exists $self->{$_} } qw(Author Maintainer);
  4         8  
111             }
112 4         6 @missing_fields = sort @missing_fields;
113              
114 4 100       10 if (@missing_fields) {
115 1         18 die "Invalid DESRIPTION. Missing mandatory fields: "
116             . join( ", ", @missing_fields );
117             }
118             }
119              
120             sub get {
121 5     5 0 867 my ( $self, $key ) = @_;
122 5         23 return $self->{$key};
123             }
124              
125             ## utlities
126              
127             sub _trim {
128 330     330   441 my ($s) = @_;
129 330         621 $s =~ s/^\s+//s;
130 330         669 $s =~ s/\s+$//s;
131 330         571 return $s;
132             }
133              
134             sub _split_list {
135 18     18   28 my ( $s, $r_sep ) = @_;
136 18   33     79 $r_sep ||= qr/,/;
137 18         44 my @lst = map { _trim($_) } split( $r_sep, _trim($s) );
  69         95  
138 18         50 return \@lst;
139             }
140              
141             1;
142              
143             __END__