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   372722 use strict;
  4         27  
  4         114  
4 4     4   20 use warnings;
  4         8  
  4         115  
5 4     4   18 use Carp;
  4         9  
  4         251  
6 4     4   25 use Exporter 5.57 'import';
  4         78  
  4         7241  
7              
8             our $VERSION = '0.04';
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 1027 my $distname = shift;
15              
16 596         883 my %res;
17              
18             # Stringify first, in case $distname is some kind of an object
19 596         1146 my $path = "$distname";
20 596         1223 $res{arg} = $path;
21              
22             # Small path normalization
23 596         1034 $path =~ s!\\!/!g;
24 596         993 $path =~ s!//+!/!g;
25 596         872 $path =~ s!/\./!/!g;
26              
27 596         1037 $path =~ s!^(.*?/)?(?:authors/)?id/!!;
28              
29             # Get pause_id
30 596         863 my ($pause_id, $author_dir);
31              
32             # A/AU/AUTHOR/Dist-Version.ext
33 596 100       1749 if ($path =~ s!^(([A-Z])/(\2[A-Z0-9])/(\3[A-Z0-9-]{0,7})/)!!) {
    100          
34 33         83 $author_dir = $1;
35 33         82 $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         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         829 $pause_id = "";
50              
51             # Assume it's a local distribution
52 562         772 $author_dir = "L/LO/LOCAL/";
53             }
54 596         989 $res{pause_id} = $pause_id;
55 596         1325 $res{cpan_path} = "$author_dir$path";
56              
57             # Now the path should be (subdir/)dist-version
58 596 100       1525 if ($path =~ s!^(.+/)!!) {
59 2         9 $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       4863 $path =~ s/($SUFFRE)//i or return;
67 593         1911 $res{extension} = $1;
68              
69 593         1039 $res{name_and_version} = $path;
70              
71             # Parse dist-version
72 593         1113 my $info = _parse_distv($path);
73 593         3940 $res{$_} = $info->{$_} for keys %$info;
74              
75 593         2803 return \%res;
76             }
77              
78             sub _parse_distv {
79 593     593   891 my $distv = shift;
80              
81 593         814 my %res;
82              
83             # Remove potential -withoutworldwriteables suffix
84 593         868 $distv =~ s/-withoutworldwriteables$//;
85              
86 593         900 my $trial;
87             # Remove TRIAL (PAUSE::dist::isa_dev_version seems to be
88             # a little too strict)
89 593 100       1251 if ($distv =~ s/([_\-])(TRIAL(?:[0-9]*|[_.\-].+))$//) {
90 2         7 $trial = [$1, $2];
91             }
92              
93             # Remove RC for perl as well
94 593         747 my $rc;
95 593 100 100     1524 if ($distv =~ /^perl/ and $distv =~ s/\-(RC[0-9]*)$//) {
96 2         7 $rc = $1;
97             }
98              
99 593         739 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       2692 if ($distv =~ s/\-((?:[vV][0-9]|[0-9.])[^-]*)$//) {
    100          
103 423         951 $version = $1;
104             }
105             # However, there may be a trailing part.
106             elsif ($distv =~ s/\-((?:[vV][0-9]|[0-9.])(?![A-Z]).*?)$//) {
107 16         38 $version = $1;
108              
109             # Special case
110 16 50 33     49 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       1816 if ($distv =~ s/([_\.-]?)([vV]?[0-9]*\.[0-9]+.*)$//) {
119 109   100     393 my $separator = $1 || '';
120 109 100       289 $version = defined $version ? "$2-$version" : $2;
121 109         186 $version =~ s/^\.//;
122              
123             # Special case
124 109 50       264 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     1584 if (!defined $version and $distv =~ s/\-([a-z]+[0-9][0-9_]*)$//) {
132 11         28 $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     2022 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         46 $version = $2;
141              
142             # Special case
143 23 50 66     72 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     1330 if (!defined $version and $distv =~ s/\-undef$//) {
150 1         3 $version = undef;
151             }
152              
153 593         889 my $dist = $distv;
154              
155 593         776 my $dev;
156 593 100 100     3379 if ($dist eq 'perl') {
    100 100        
157 5 50       30 if ($version =~ /\d\.(\d+)(?:\D(\d+))?/) {
158 5 50 66     50 $dev = 1 if ($1 > 6 and $1 & 1) or ($2 and $2 >= 50);
      66        
      33        
159             }
160 5 100       13 if ($rc) {
161 2         8 $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 593 100       1077 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         879 $dist =~ s/\.pm$//;
177              
178             # Remove apparent remnants that can't be a part of a package name
179 593         1251 $dist =~ s/[\-\.]+$//;
180              
181 593         771 my $version_number;
182 593 100       1096 if (defined $version) {
183 564 100       1812 if ($version =~ /^([vV]?[0-9._]+)(?:\-|$)/) {
184 449         837 $version_number = $1;
185 449         941 $version_number =~ s/[\._]+$//;
186             }
187             }
188              
189             return {
190 593         2767 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 687132 my ($class, $distname) = @_;
201 591   50     1437 my $info = parse_distname($distname) || {};
202 591         1720 bless $info, $class;
203             }
204              
205             sub distname_info {
206 1     1 0 2603 my $distname = shift;
207 1         3 my $info = parse_distname($distname);
208 1         11 @$info{qw/name version is_dev/};
209             }
210              
211 621     621 0 16713 sub dist { shift->{name} }
212 621     621 1 16345 sub version { shift->{version} }
213 621 100   621 0 16745 sub maturity { shift->{is_dev} ? 'developer' : 'released' }
214             sub filename {
215 61     61 0 13547 my $self = shift;
216 61         358 join "", grep defined $_, @$self{qw/subdir name_and_version extension/};
217             }
218 61     61 0 10839 sub cpanid { shift->{pause_id} }
219 61     61 0 15082 sub distvname { shift->{name_and_version} }
220 61     61 1 14350 sub extension { substr(shift->{extension}, 1) }
221 61     61 0 12597 sub pathname { shift->{arg} }
222              
223             sub properties {
224 30     30 0 99 my $self = shift;
225 30         85 my @methods = qw/
226             dist version maturity filename
227             cpanid distvname extension pathname
228             /;
229 30         34 my %properties;
230 30         55 for my $method (@methods) {
231 240         438 $properties{$method} = $self->$method;
232             }
233 30         198 %properties;
234             }
235              
236             # extra accessors
237              
238 1     1 1 7 sub is_perl6 { shift->{is_perl6} }
239 1     1 1 6 sub version_number { shift->{version_number} }
240              
241             1;
242              
243             __END__