File Coverage

lib/Parse/Distname.pm
Criterion Covered Total %
statement 109 113 96.4
branch 47 54 87.0
condition 31 40 77.5
subroutine 19 19 100.0
pod 4 14 28.5
total 210 240 87.5


line stmt bran cond sub pod time code
1             package Parse::Distname;
2              
3 4     4   369375 use strict;
  4         28  
  4         118  
4 4     4   20 use warnings;
  4         6  
  4         112  
5 4     4   19 use Carp;
  4         8  
  4         237  
6 4     4   24 use Exporter 5.57 'import';
  4         70  
  4         6977  
7              
8             our $VERSION = '0.03';
9             our @EXPORT_OK = qw/parse_distname/;
10              
11             our $SUFFRE = qr/\.(?:tgz|tbz|tar[\._-]gz|tar\.bz2|tar\.Z|zip)$/;
12              
13             sub parse_distname {
14 596     596 0 1049 my $distname = shift;
15              
16 596         859 my %res;
17              
18             # Stringify first, in case $distname is some kind of an object
19 596         1107 my $path = "$distname";
20 596         1300 $res{arg} = $path;
21              
22             # Small path normalization
23 596         1104 $path =~ s!\\!/!g;
24 596         970 $path =~ s!//+!/!g;
25 596         831 $path =~ s!/\./!/!g;
26              
27 596         1038 $path =~ s!^(.*?/)?(?:authors/)?id/!!;
28              
29             # Get pause_id
30 596         839 my ($pause_id, $author_dir);
31              
32             # A/AU/AUTHOR/Dist-Version.ext
33 596 100       1648 if ($path =~ s!^(([A-Z])/(\2[A-Z0-9])/(\3[A-Z0-9-]{0,7})/)!!) {
    100          
34 33         86 $author_dir = $1;
35 33         80 $pause_id = $4;
36             }
37             # AUTHOR/Dist-Version.ext as a handy shortcut (esp. for testing)
38             elsif ($path =~ s!^([A-Z][A-Z0-9][A-Z0-9-]{0,7})/!!) {
39 1         5 $pause_id = $1;
40 1         6 $author_dir = join '/',
41             substr($pause_id, 0, 1),
42             substr($pause_id, 0, 2),
43             $pause_id,
44             "";
45             }
46             # A little backward incompatibility here (id/A/AU/AUTHOR etc)
47             # but I believe nobody cares.
48             else {
49 562         871 $pause_id = "";
50              
51             # Assume it's a local distribution
52 562         770 $author_dir = "L/LO/LOCAL/";
53             }
54 596         946 $res{pause_id} = $pause_id;
55 596         1233 $res{cpan_path} = "$author_dir$path";
56              
57             # Now the path should be (subdir/)dist-version
58 596 100       1445 if ($path =~ s!^(.+/)!!) {
59 2         7 $res{subdir} = $1;
60              
61             # Typical Perl6 distributions are located under Perl6/ directory
62 2 50       9 $res{perl6} = 1 if $res{subdir} =~ m!^Perl6/!;
63             }
64              
65             # PAUSE allows only a few extensions ($PAUSE::dist::SUFFQR + zip)
66 596 100       4674 $path =~ s/($SUFFRE)//i or return;
67 593         1731 $res{extension} = $1;
68              
69 593         1051 $res{name_and_version} = $path;
70              
71             # Parse dist-version
72 593         1079 my $info = _parse_distv($path);
73 593         3799 $res{$_} = $info->{$_} for keys %$info;
74              
75 593         2652 return \%res;
76             }
77              
78             sub _parse_distv {
79 593     593   997 my $distv = shift;
80              
81 593         816 my %res;
82              
83             # Remove potential -withoutworldwriteables suffix
84 593         858 $distv =~ s/-withoutworldwriteables$//;
85              
86 593         803 my $trial;
87             # Remove TRIAL (PAUSE::dist::isa_dev_version seems to be
88             # a little too strict)
89 593 100       1236 if ($distv =~ s/([_\-])(TRIAL(?:[0-9]*|[_.\-].+))$//) {
90 2         9 $trial = [$1, $2];
91             }
92              
93             # Remove RC for perl as well
94 593         736 my $rc;
95 593 100 100     1498 if ($distv =~ /^perl/ and $distv =~ s/\-(RC[0-9]*)$//) {
96 2         7 $rc = $1;
97             }
98              
99 593         702 my $version;
100             # Usually a version, which starts with a number (or a 'v'-number),
101             # is the last part of the name.
102 593 100       2674 if ($distv =~ s/\-((?:[vV][0-9]|[0-9.])[^-]*)$//) {
    100          
103 423         879 $version = $1;
104             }
105             # However, there may be a trailing part.
106             elsif ($distv =~ s/\-((?:[vV][0-9]|[0-9.])(?![A-Z]).*?)$//) {
107 16         35 $version = $1;
108              
109             # Special case
110 16 50 33     53 if ($distv eq 'perl' and $version !~ /\./) {
111 0         0 $distv = "$distv-$version";
112 0         0 $version = undef;
113             }
114             }
115              
116             # If the name still contains a dot between numbers,
117             # it's probably a part of the version.
118 593 100       1716 if ($distv =~ s/([_\.-]?)([vV]?[0-9]*\.[0-9]+.*)$//) {
119 109   100     368 my $separator = $1 || '';
120 109 100       273 $version = defined $version ? "$2-$version" : $2;
121 109         170 $version =~ s/^\.//;
122              
123             # Special case
124 109 50       266 if ($distv =~ s/_v$//) {
125 0         0 $version = "v$separator$version";
126             }
127             }
128              
129             # If we still don't have a version and the name has a tailing number
130             # with a small-letter prefix (other than 'v')
131 593 100 100     1634 if (!defined $version and $distv =~ s/\-([a-z]+[0-9][0-9_]*)$//) {
132 11         26 $version = $1;
133             }
134              
135             # If we still don't have a version, and the name doesn't have a hyphen,
136             # and it has a tailing number... (and an occasional alpha/beta marker)
137             # (and the number is not a part of a few proper names)
138 593 100 66     1956 if (!defined $version and $distv !~ /\-(?:S3|MSWin32|OS2)$/i and $distv =~ s/([_\.]?)([vV]?[0-9_]+[ab]?)$//) {
      100        
139 23         52 my $separator = $1;
140 23         41 $version = $2;
141              
142             # Special case
143 23 50 66     76 if (!$separator and $distv =~ s/_([a-z])$//) {
144 0         0 $version = "$1$version";
145             }
146             }
147              
148             # Special case that should be put at the end
149 593 100 100     1228 if (!defined $version and $distv =~ s/\-undef$//) {
150 1         3 $version = undef;
151             }
152              
153 593         892 my $dist = $distv;
154              
155 593         1101 my $dev;
156 593 100 100     3202 if ($dist eq 'perl') {
    100 100        
157 5 50       33 if ($version =~ /\d\.(\d+)(?:\D(\d+))?/) {
158 5 50 66     61 $dev = 1 if ($1 > 6 and $1 & 1) or ($2 and $2 >= 50);
      66        
      33        
159             }
160 5 100       15 if ($rc) {
161 2         9 $version = "$version-$rc";
162 2         6 $dev = 1;
163             }
164             }
165             elsif (($version and $version =~ /\d\.\d+_\d/) or $trial) {
166 6         14 $dev = 1;
167             }
168              
169 593 100       1065 if ($trial) {
170 2 50       8 $version = defined $version ? "$version$trial->[0]$trial->[1]" : $trial->[1];
171 2         5 $dev = 1;
172             }
173              
174             # Normalize the Dist.pm-1.23 convention which CGI.pm and
175             # a few others use.
176 593         856 $dist =~ s/\.pm$//;
177              
178             # Remove apparent remnants that can't be a part of a package name
179 593         1320 $dist =~ s/[\-\.]+$//;
180              
181 593         763 my $version_number;
182 593 100       1140 if (defined $version) {
183 564 100       1855 if ($version =~ /^([vV]?[0-9._]+)(?:\-|$)/) {
184 449         797 $version_number = $1;
185 449         921 $version_number =~ s/[\._]+$//;
186             }
187             }
188              
189             return {
190 593         2617 name => $dist,
191             version => $version,
192             version_number => $version_number,
193             is_dev => $dev,
194             };
195             }
196              
197             # for compatibility with CPAN::DistnameInfo
198              
199             sub new {
200 591     591 0 671635 my ($class, $distname) = @_;
201 591   50     1337 my $info = parse_distname($distname) || {};
202 591         1641 bless $info, $class;
203             }
204              
205             sub distname_info {
206 1     1 0 2442 my $distname = shift;
207 1         3 my $info = parse_distname($distname);
208 1         19 @$info{qw/name version is_dev/};
209             }
210              
211 621     621 0 16738 sub dist { shift->{name} }
212 621     621 1 16190 sub version { shift->{version} }
213 621 100   621 0 16935 sub maturity { shift->{is_dev} ? 'developer' : 'released' }
214             sub filename {
215 61     61 0 13149 my $self = shift;
216 61         339 join "", grep defined $_, @$self{qw/subdir name_and_version extension/};
217             }
218 61     61 0 15738 sub cpanid { shift->{pause_id} }
219 61     61 0 11283 sub distvname { shift->{name_and_version} }
220 61     61 1 12722 sub extension { substr(shift->{extension}, 1) }
221 61     61 0 10818 sub pathname { shift->{arg} }
222              
223             sub properties {
224 30     30 0 118 my $self = shift;
225 30         120 my @methods = qw/
226             dist version maturity filename
227             cpanid distvname extension pathname
228             /;
229 30         40 my %properties;
230 30         53 for my $method (@methods) {
231 240         442 $properties{$method} = $self->$method;
232             }
233 30         203 %properties;
234             }
235              
236             # extra accessors
237              
238 1     1 1 5 sub is_perl6 { shift->{is_perl6} }
239 1     1 1 5 sub version_number { shift->{version_number} }
240              
241             1;
242              
243             __END__