File Coverage

blib/lib/Perl/Version.pm
Criterion Covered Total %
statement 182 182 100.0
branch 59 60 98.3
condition 25 29 86.2
subroutine 25 25 100.0
pod 10 10 100.0
total 301 306 98.3


line stmt bran cond sub pod time code
1             package Perl::Version;
2              
3 5     5   83853 use warnings;
  5         11  
  5         161  
4 5     5   26 use strict;
  5         11  
  5         154  
5 5     5   28 use Carp;
  5         15  
  5         585  
6 5     5   30 use Scalar::Util qw( blessed );
  5         10  
  5         1059  
7              
8             our $VERSION = '1.013';
9              
10             use overload (
11 5         62 '""' => \&stringify,
12             '<=>' => \&vcmp,
13             'cmp' => \&vcmp,
14 5     5   10057 );
  5         6190  
15              
16 5         612 use constant REGEX => qr/ ( (?i: Revision: \s+ ) | v | )
17             ( \d+ (?: [.] \d+)* )
18 5     5   871 ( (?: _ \d+ )? ) /x;
  5         91  
19              
20 5     5   27 use constant MATCH => qr/ ^ ( \s* ) @{[ REGEX ]} ( \s* ) $ /x;
  5         8  
  5         10  
  5         1264  
21              
22             my %NORMAL_FORMAT = (
23             prefix => 'v',
24             printf => ['%d'],
25             extend => '.%d',
26             alpha => '_%02d',
27             suffix => '',
28             fields => 3,
29             );
30              
31             my %NUMERIC_FORMAT = (
32             prefix => '',
33             printf => [ '%d', '.%03d' ],
34             extend => '%03d',
35             alpha => '_%02d',
36             suffix => '',
37             fields => 2,
38             );
39              
40             my %COMPONENT_NAME;
41              
42             BEGIN {
43 5     5   27 %COMPONENT_NAME = (
44             revision => 0,
45             version => 1,
46             subversion => 2
47             );
48              
49             # Make accessors
50 5         26 my @fields = ( keys %COMPONENT_NAME, qw( alpha ) );
51              
52 5     5   33 no strict 'refs';
  5         10  
  5         752  
53              
54 5         69 for my $field ( @fields ) {
55             *$field = sub {
56 108     108   105813 my $self = shift;
57 108         328 return $self->component( $field, @_ );
58 20         129 };
59              
60 20         40 my $inc_func = "inc_$field";
61             *$inc_func = sub {
62 13     13   108 my $self = shift;
63 13         146 return $self->increment( $field );
64 20         20840 };
65             }
66             }
67              
68             sub new {
69 310     310 1 169267 my $class = shift;
70 310   66     4945 my $self
71             = bless {}, ref $class
72             || $class
73             || croak "new must be called as a class or object method";
74              
75 309         2425 $self->{version} = [0];
76              
77 309 100       1914 $self->_parse( @_ ) if @_;
78              
79 308         1481 return $self;
80             }
81              
82             sub _resolve_component_name {
83 319     319   637 my $self = shift;
84 319         533 my $name = shift;
85              
86 319 100       3078 if ( $name =~ /^-?\d+$/ ) {
87             # Allow negative subscripts
88 278 100       628 $name += $self->components if $name < 0;
89 278         659 return $name;
90             }
91              
92 41 100       573 croak "Unknown component name: $name"
93             unless exists $COMPONENT_NAME{ lc( $name ) };
94              
95 39         132 return $COMPONENT_NAME{ lc( $name ) };
96             }
97              
98             sub _guess_num_format {
99 836     836   1262 my $self = shift;
100 836         1876 my $num = shift;
101              
102 836 100       4768 if ( $num =~ m{ ^ 0 \d }x ) {
103 122         1124 return '%0' . length( $num ) . 'd';
104             }
105              
106 714         4270 return '%d';
107             }
108              
109             sub _parse {
110 307     307   1385 my $self = shift;
111              
112             # Check for vstring before anything else happens
113 307 100 66     2454 if ( $] >= 5.008_001 && Scalar::Util::isvstring $_[0] ) {
114 3         25 $self->{format} = {%NORMAL_FORMAT};
115 3         15 my @parts = map { ord } split //, shift;
  9         21  
116 3         9 $self->{version} = \@parts;
117 3         7 return;
118             }
119              
120 304         2502 my $version = join( ' ', map { "$_" } @_ );
  304         1762  
121              
122 304 100       2999 croak "Illegal version string: $version"
123             unless $version =~ MATCH;
124              
125 303         1507 my $format = { fields => 1 };
126 303         2616 my ( $pad, $pfx, $ver, $alp, $sfx ) = ( $1, $2, $3, $4, $5 );
127              
128             # Decode version into format
129 303         1271 $format->{prefix} = $pad . $pfx;
130 303         1045 $format->{suffix} = $sfx;
131              
132 303         4104 my @parts = split( /[.]/, $ver );
133 303         1588 my @ver = ( shift( @parts ) + 0 );
134              
135 303         1104 my @fmt = ( $self->_guess_num_format( $ver[0] ) );
136              
137 303 100 100     2751 if ( @parts == 1 && length( $parts[0] ) >= 3 ) {
138              
139 48         626 my $threes = pop @parts;
140 48         285 my @cluster = ( $threes =~ /(\d{1,3})/g );
141              
142             # warn "# $threes <", join( '>, <', @cluster ), ">\n";
143 48         117 push @fmt, map { $self->_guess_num_format( $_ ) } @cluster;
  75         385  
144 48         103 $fmt[1] = '.' . $fmt[1];
145 48         96 $format->{extend} = '%03d';
146              
147 48         880 push @parts, map { 0 + $_ } @cluster;
  75         208  
148             }
149             else {
150              
151             # Parts with leading zeros
152 255         1116 my @lz = grep { m{ ^ 0 \d }x } @parts;
  416         1529  
153              
154             # Work out how many different lengths we have
155 255         498 my %le = map { length( $_ ) => 1 } @parts;
  416         2469  
156              
157 255 100 100     1305 if ( @lz && keys %le == 1 ) {
158 18         50 push @fmt,
159             ( '.' . $self->_guess_num_format( shift @lz ) ) x @parts;
160             }
161             else {
162 237         396 push @fmt, map { '.' . $self->_guess_num_format( $_ ) } @parts;
  383         1630  
163             }
164              
165 255 100       2144 $format->{extend} = ( @parts ? '' : '.' ) . $fmt[-1];
166             }
167              
168 303         765 $format->{printf} = \@fmt;
169              
170 303 100       618 if ( length( $alp ) ) {
171 57 50       416 die "Badly formatted alpha got through"
172             unless $alp =~ m{ _ (\d+) }x;
173              
174 57         114 my $alpha = $1;
175              
176 57         141 $self->{alpha} = $alpha + 0;
177 57         136 $format->{alpha} = '_' . $self->_guess_num_format( $alpha );
178             }
179             else {
180 246         961 $format->{alpha} = $NORMAL_FORMAT{alpha};
181             }
182              
183 303         543 $self->{format} = $format;
184              
185 303         441 push @ver, map { $_ + 0 } @parts;
  491         3795  
186              
187 303         1016 $self->{version} = \@ver;
188              
189 303         1272 return;
190             }
191              
192             sub _format {
193 264     264   1592 my $self = shift;
194 264         390 my $format = shift;
195              
196 264         910 my @result = ();
197              
198 264         355 my @parts = @{ $self->{version} };
  264         807  
199 264         593 my @fmt = @{ $format->{printf} };
  264         873  
200              
201 264         1181 push @parts, 0 while @parts < $format->{fields};
202              
203             # Adjust the format to be the same length as the number of fields
204 264         815 pop @fmt while @fmt > @parts;
205 264         1434 push @fmt, $format->{extend} while @parts > @fmt;
206              
207 264         4140 my $version
208             = ( $format->{prefix} )
209             . sprintf( join( '', @fmt ), @parts )
210             . ( $format->{suffix} );
211              
212 264 100       986 $version .= sprintf( $format->{alpha}, $self->{alpha} )
213             if defined $self->{alpha};
214              
215 264         1109 push @result, $version;
216              
217 264         2238 return join( ' ', @result );
218             }
219              
220             sub stringify {
221 137     137 1 274340 my $self = shift;
222 137   100     4037 return $self->_format( $self->{format} || \%NORMAL_FORMAT );
223             }
224              
225             sub normal {
226 63     63 1 22628 return shift->_format( \%NORMAL_FORMAT );
227             }
228              
229             sub numify {
230 64     64 1 4897 return shift->_format( \%NUMERIC_FORMAT );
231             }
232              
233             sub is_alpha {
234 1     1 1 1720 my $self = shift;
235 1         5 return exists $self->{alpha};
236             }
237              
238             sub vcmp {
239 423     423 1 5586 my ( $self, $other, $rev ) = @_;
240              
241             # Promote to object
242 423 100       1086 $other = __PACKAGE__->new( $other ) unless ref $other;
243              
244 423 100 100     4092 croak "Can't compare with $other"
245             unless blessed $other && $other->isa( __PACKAGE__ );
246              
247 421 100       1072 return $other->vcmp( $self, 0 ) if $rev;
248              
249 418         641 my @this = @{ $self->{version} };
  418         19155  
250 418         549 my @that = @{ $other->{version} };
  418         9986  
251              
252 418         1281 push @this, 0 while @this < @that;
253 418         2268 push @that, 0 while @that < @this;
254              
255 418         2323 while ( @this ) {
256 1159 100       6271 if ( my $cmp = ( shift( @this ) <=> shift( @that ) ) ) {
257 55         272 return $cmp;
258             }
259             }
260              
261 363   100     3966 return ( $self->{alpha} || 0 ) <=> ( $other->{alpha} || 0 );
      100        
262             }
263              
264             sub components {
265 392     392 1 2499 my $self = shift;
266              
267 392 100       979 if ( @_ ) {
268 9         20 my $fields = shift;
269              
270 9 100       33 if ( ref $fields eq 'ARRAY' ) {
271 3         19 $self->{version} = [@$fields];
272             }
273             else {
274 6 100       205 croak "Can't set the number of components to 0"
275             unless $fields;
276              
277             # Adjust the number of fields
278 5         8 pop @{ $self->{version} }, while @{ $self->{version} } > $fields;
  6         23  
  1         4  
279 9         41 push @{ $self->{version} }, 0,
  4         10  
280 5         10 while @{ $self->{version} } < $fields;
281             }
282             }
283             else {
284 383         2563 return @{ $self->{version} };
  383         1425  
285             }
286             }
287              
288             sub component {
289 387     387 1 8823 my $self = shift;
290 387         756 my $field = shift;
291              
292 387 100       1308 defined $field or croak "You must specify a component number";
293              
294 386 100       1070 if ( lc( $field ) eq 'alpha' ) {
295 95 100       265 if ( @_ ) {
296 34         70 my $alpha = shift;
297 34 100       85 if ( $alpha ) {
298 5         27 $self->{alpha} = $alpha;
299             }
300             else {
301 29         168 delete $self->{alpha};
302             }
303             }
304             else {
305 61   100     777 return $self->{alpha} || 0;
306             }
307             }
308             else {
309 291         766 $field = $self->_resolve_component_name( $field );
310 290         784 my $fields = $self->components;
311              
312 290 100       624 if ( @_ ) {
313 61 100       158 if ( $field >= $fields ) {
314              
315             # Extend array if necessary
316 2         7 $self->components( $field + 1 );
317             }
318              
319 61         293 $self->{version}->[$field] = shift;
320             }
321             else {
322 229 100 66     1283 return unless $field >= 0 && $field < $fields;
323 171         987 return $self->{version}->[$field];
324             }
325             }
326             }
327              
328             sub increment {
329 32     32 1 605 my $self = shift;
330 32         68 my $field = shift;
331 32         1345 my $fields = $self->components;
332              
333 32 100       140 if ( lc( $field ) eq 'alpha' ) {
334 4         14 $self->alpha( $self->alpha + 1 );
335             }
336             else {
337 28         98 $field = $self->_resolve_component_name( $field );
338              
339 27 100 66     301 croak "Component $field is out of range 0..", $fields - 1
340             if $field < 0 || $field >= $fields;
341              
342             # Increment the field
343 26         85 $self->component( $field, $self->component( $field ) + 1 );
344              
345             # Zero out any following fields
346 26         81 while ( ++$field < $fields ) {
347 28         58 $self->component( $field, 0 );
348             }
349 26         79 $self->alpha( 0 );
350             }
351             }
352              
353             sub set {
354 2     2 1 4 my $self = shift;
355 2         5 my $other = shift;
356              
357 2 100       10 $other = __PACKAGE__->new( $other ) unless ref $other;
358              
359 2         9 my @comp = $other->components;
360              
361 2         7 $self->components( \@comp );
362 2         9 $self->alpha( $other->alpha );
363             }
364              
365             1;
366             __END__