File Coverage

blib/lib/R/DescriptionFile.pm
Criterion Covered Total %
statement 77 77 100.0
branch 17 20 85.0
condition 7 9 77.7
subroutine 13 13 100.0
pod 0 4 0.0
total 114 123 92.6


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