File Coverage

lib/Parse/Distname.pm
Criterion Covered Total %
statement 109 113 96.4
branch 48 54 88.8
condition 31 40 77.5
subroutine 19 19 100.0
pod 4 14 28.5
total 211 240 87.9


line stmt bran cond sub pod time code
1             package Parse::Distname;
2              
3 4     4   323821 use strict;
  4         27  
  4         96  
4 4     4   18 use warnings;
  4         5  
  4         89  
5 4     4   15 use Carp;
  4         6  
  4         203  
6 4     4   19 use Exporter 5.57 'import';
  4         64  
  4         5790  
7              
8             our $VERSION = '0.05';
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 598     598 0 986 my $distname = shift;
15              
16 598         727 my %res;
17              
18             # Stringify first, in case $distname is some kind of an object
19 598         983 my $path = "$distname";
20 598         1066 $res{arg} = $path;
21              
22             # Small path normalization
23 598         1031 $path =~ s!\\!/!g;
24 598         891 $path =~ s!//+!/!g;
25 598         717 $path =~ s!/\./!/!g;
26              
27 598         843 $path =~ s!^(.*?/)?(?:authors/)?id/!!;
28              
29             # Get pause_id
30 598         1018 my ($pause_id, $author_dir);
31              
32             # A/AU/AUTHOR/Dist-Version.ext
33 598 100       1409 if ($path =~ s!^(([A-Z])/(\2[A-Z0-9])/(\3[A-Z0-9-]{0,7})/)!!) {
    100          
34 35         76 $author_dir = $1;
35 35         69 $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         4 $pause_id = $1;
40 1         5 $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         659 $pause_id = "";
50              
51             # Assume it's a local distribution
52 562         804 $author_dir = "L/LO/LOCAL/";
53             }
54 598         843 $res{pause_id} = $pause_id;
55 598         1333 $res{cpan_path} = "$author_dir$path";
56              
57             # Now the path should be (subdir/)dist-version
58 598 100       1514 if ($path =~ s!^(.+/)!!) {
59 4         10 $res{subdir} = $1;
60              
61             # Typical Perl6 distributions are located under Perl6/ directory
62 4 100       13 $res{perl6} = 1 if $res{subdir} =~ m!^Perl6/!;
63             }
64              
65             # PAUSE allows only a few extensions ($PAUSE::dist::SUFFQR + zip)
66 598 100       4359 $path =~ s/($SUFFRE)//i or return;
67 595         1526 $res{extension} = $1;
68              
69 595         860 $res{name_and_version} = $path;
70              
71             # Parse dist-version
72 595         844 my $info = _parse_distv($path);
73 595         4031 $res{$_} = $info->{$_} for keys %$info;
74              
75 595         2393 return \%res;
76             }
77              
78             sub _parse_distv {
79 595     595   910 my $distv = shift;
80              
81 595         689 my %res;
82              
83             # Remove potential -withoutworldwriteables suffix
84 595         874 $distv =~ s/-withoutworldwriteables$//;
85              
86 595         713 my $trial;
87             # Remove TRIAL (PAUSE::dist::isa_dev_version seems to be
88             # a little too strict)
89 595 100       1179 if ($distv =~ s/([_\-])(TRIAL(?:[0-9]*|[_.\-].+))$//) {
90 2         5 $trial = [$1, $2];
91             }
92              
93             # Remove RC for perl as well
94 595         677 my $rc;
95 595 100 100     1275 if ($distv =~ /^perl/ and $distv =~ s/\-(RC[0-9]*)$//) {
96 2         5 $rc = $1;
97             }
98              
99 595         634 my $version;
100             # Usually a version, which starts with a number (or a 'v'-number),
101             # is the last part of the name.
102 595 100       2346 if ($distv =~ s/\-((?:[vV][0-9]|[0-9.])[^-]*)$//) {
    100          
103 425         748 $version = $1;
104             }
105             # However, there may be a trailing part.
106             elsif ($distv =~ s/\-((?:[vV][0-9]|[0-9.])(?![A-Z]).*?)$//) {
107 16         34 $version = $1;
108              
109             # Special case
110 16 50 33     75 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 595 100       1625 if ($distv =~ s/([_\.-]?)([vV]?[0-9]*\.[0-9]+.*)$//) {
119 109   100     369 my $separator = $1 || '';
120 109 100       252 $version = defined $version ? "$2-$version" : $2;
121 109         166 $version =~ s/^\.//;
122              
123             # Special case
124 109 50       222 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 595 100 100     1406 if (!defined $version and $distv =~ s/\-([a-z]+[0-9][0-9_]*)$//) {
132 11         22 $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 595 100 66     1884 if (!defined $version and $distv !~ /\-(?:S3|MSWin32|OS2|(?:[A-Za-z][A-Za-z0-9_]*)?SSL3)$/i and $distv =~ s/([_\.]?)([vV]?[0-9_]+[ab]?)$//) {
      100        
139 23         52 my $separator = $1;
140 23         44 $version = $2;
141              
142             # Special case
143 23 50 66     68 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 595 100 100     997 if (!defined $version and $distv =~ s/\-undef$//) {
150 1         2 $version = undef;
151             }
152              
153 595         755 my $dist = $distv;
154              
155 595         619 my $dev;
156 595 100 100     2867 if ($dist eq 'perl') {
    100 100        
157 5 50       26 if ($version =~ /\d\.(\d+)(?:\D(\d+))?/) {
158 5 50 66     45 $dev = 1 if ($1 > 6 and $1 & 1) or ($2 and $2 >= 50);
      66        
      33        
159             }
160 5 100       14 if ($rc) {
161 2         7 $version = "$version-$rc";
162 2         4 $dev = 1;
163             }
164             }
165             elsif (($version and $version =~ /\d\.\d+_\d/) or $trial) {
166 6         13 $dev = 1;
167             }
168              
169 595 100       888 if ($trial) {
170 2 50       8 $version = defined $version ? "$version$trial->[0]$trial->[1]" : $trial->[1];
171 2         4 $dev = 1;
172             }
173              
174             # Normalize the Dist.pm-1.23 convention which CGI.pm and
175             # a few others use.
176 595         739 $dist =~ s/\.pm$//;
177              
178             # Remove apparent remnants that can't be a part of a package name
179 595         1046 $dist =~ s/[\-\.]+$//;
180              
181 595         608 my $version_number;
182 595 100       896 if (defined $version) {
183 566 100       1475 if ($version =~ /^([vV]?[0-9._]+)(?:\-|$)/) {
184 451         675 $version_number = $1;
185 451         814 $version_number =~ s/[\._]+$//;
186             }
187             }
188              
189             return {
190 595         2468 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 592     592 0 706363 my ($class, $distname) = @_;
201 592   50     1193 my $info = parse_distname($distname) || {};
202 592         1435 bless $info, $class;
203             }
204              
205             sub distname_info {
206 1     1 0 2374 my $distname = shift;
207 1         4 my $info = parse_distname($distname);
208 1         23 @$info{qw/name version is_dev/};
209             }
210              
211 622     622 0 15418 sub dist { shift->{name} }
212 622     622 1 14963 sub version { shift->{version} }
213 622 100   622 0 16252 sub maturity { shift->{is_dev} ? 'developer' : 'released' }
214             sub filename {
215 62     62 0 17478 my $self = shift;
216 62         303 join "", grep defined $_, @$self{qw/subdir name_and_version extension/};
217             }
218 62     62 0 15794 sub cpanid { shift->{pause_id} }
219 62     62 0 13208 sub distvname { shift->{name_and_version} }
220 62     62 1 13681 sub extension { substr(shift->{extension}, 1) }
221 62     62 0 9765 sub pathname { shift->{arg} }
222              
223             sub properties {
224 30     30 0 573 my $self = shift;
225 30         80 my @methods = qw/
226             dist version maturity filename
227             cpanid distvname extension pathname
228             /;
229 30         34 my %properties;
230 30         45 for my $method (@methods) {
231 240         473 $properties{$method} = $self->$method;
232             }
233 30         166 %properties;
234             }
235              
236             # extra accessors
237              
238 2     2 1 9 sub is_perl6 { shift->{perl6} }
239 2     2 1 7 sub version_number { shift->{version_number} }
240              
241             1;
242              
243             __END__