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   35121 use warnings;
  5         11  
  5         137  
4 5     5   24 use strict;
  5         9  
  5         101  
5 5     5   22 use Carp;
  5         11  
  5         385  
6 5     5   25 use Scalar::Util qw( blessed );
  5         7  
  5         796  
7              
8             our $VERSION = '1.013_03';
9              
10             use overload (
11 5         36 '""' => \&stringify,
12             '<=>' => \&vcmp,
13             'cmp' => \&vcmp,
14 5     5   6335 );
  5         4554  
15              
16 5         543 use constant REGEX => qr/ ( (?i: Revision: \s+ ) | v | )
17             ( \d+ (?: [.] \d+)* )
18 5     5   701 ( (?: _ \d+ )? ) /x;
  5         15  
19              
20 5     5   26 use constant MATCH => qr/ ^ ( \s* ) @{[ REGEX ]} ( \s* ) $ /x;
  5         10  
  5         10  
  5         941  
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   21 %COMPONENT_NAME = (
44             revision => 0,
45             version => 1,
46             subversion => 2
47             );
48              
49             # Make accessors
50 5         24 my @fields = ( keys %COMPONENT_NAME, qw( alpha ) );
51              
52 5     5   25 no strict 'refs';
  5         10  
  5         665  
53              
54 5         45 for my $field ( @fields ) {
55             *$field = sub {
56 108     108   38049 my $self = shift;
57 108         250 return $self->component( $field, @_ );
58 20         109 };
59              
60 20         38 my $inc_func = "inc_$field";
61             *$inc_func = sub {
62 13     13   22 my $self = shift;
63 13         52 return $self->increment( $field );
64 20         9786 };
65             }
66             }
67              
68             sub new {
69 310     310 1 59510 my $class = shift;
70 310   66     1791 my $self
71             = bless {}, ref $class
72             || $class
73             || croak "new must be called as a class or object method";
74              
75 309         891 $self->{version} = [0];
76              
77 309 100       1065 $self->_parse( @_ ) if @_;
78              
79 308         1100 return $self;
80             }
81              
82             sub _resolve_component_name {
83 319     319   404 my $self = shift;
84 319         467 my $name = shift;
85              
86 319 100       1231 if ( $name =~ /^-?\d+$/ ) {
87             # Allow negative subscripts
88 278 100       653 $name += $self->components if $name < 0;
89 278         661 return $name;
90             }
91              
92             croak "Unknown component name: $name"
93 41 100       345 unless exists $COMPONENT_NAME{ lc( $name ) };
94              
95 39         86 return $COMPONENT_NAME{ lc( $name ) };
96             }
97              
98             sub _guess_num_format {
99 836     836   1018 my $self = shift;
100 836         1044 my $num = shift;
101              
102 836 100       1978 if ( $num =~ m{ ^ 0 \d }x ) {
103 122         415 return '%0' . length( $num ) . 'd';
104             }
105              
106 714         1876 return '%d';
107             }
108              
109             sub _parse {
110 307     307   383 my $self = shift;
111              
112             # Check for vstring before anything else happens
113 307 100 66     1589 if ( $] >= 5.008_001 && Scalar::Util::isvstring $_[0] ) {
114 3         18 $self->{format} = {%NORMAL_FORMAT};
115 3         13 my @parts = map { ord } split //, shift;
  9         19  
116 3         7 $self->{version} = \@parts;
117 3         7 return;
118             }
119              
120 304         489 my $version = join( ' ', map { "$_" } @_ );
  304         848  
121              
122 304 100       1908 croak "Illegal version string: $version"
123             unless $version =~ MATCH;
124              
125 303         672 my $format = { fields => 1 };
126 303         997 my ( $pad, $pfx, $ver, $alp, $sfx ) = ( $1, $2, $3, $4, $5 );
127              
128             # Decode version into format
129 303         648 $format->{prefix} = $pad . $pfx;
130 303         525 $format->{suffix} = $sfx;
131              
132 303         779 my @parts = split( /[.]/, $ver );
133 303         772 my @ver = ( shift( @parts ) + 0 );
134              
135 303         728 my @fmt = ( $self->_guess_num_format( $ver[0] ) );
136              
137 303 100 100     1063 if ( @parts == 1 && length( $parts[0] ) >= 3 ) {
138              
139 48         58 my $threes = pop @parts;
140 48         195 my @cluster = ( $threes =~ /(\d{1,3})/g );
141              
142             # warn "# $threes <", join( '>, <', @cluster ), ">\n";
143 48         81 push @fmt, map { $self->_guess_num_format( $_ ) } @cluster;
  75         145  
144 48         87 $fmt[1] = '.' . $fmt[1];
145 48         95 $format->{extend} = '%03d';
146              
147 48         71 push @parts, map { 0 + $_ } @cluster;
  75         178  
148             }
149             else {
150              
151             # Parts with leading zeros
152 255         378 my @lz = grep { m{ ^ 0 \d }x } @parts;
  416         966  
153              
154             # Work out how many different lengths we have
155 255         422 my %le = map { length( $_ ) => 1 } @parts;
  416         1101  
156              
157 255 100 100     845 if ( @lz && keys %le == 1 ) {
158 18         44 push @fmt,
159             ( '.' . $self->_guess_num_format( shift @lz ) ) x @parts;
160             }
161             else {
162 237         345 push @fmt, map { '.' . $self->_guess_num_format( $_ ) } @parts;
  383         750  
163             }
164              
165 255 100       992 $format->{extend} = ( @parts ? '' : '.' ) . $fmt[-1];
166             }
167              
168 303         575 $format->{printf} = \@fmt;
169              
170 303 100       556 if ( length( $alp ) ) {
171 57 50       216 die "Badly formatted alpha got through"
172             unless $alp =~ m{ _ (\d+) }x;
173              
174 57         98 my $alpha = $1;
175              
176 57         102 $self->{alpha} = $alpha + 0;
177 57         120 $format->{alpha} = '_' . $self->_guess_num_format( $alpha );
178             }
179             else {
180 246         471 $format->{alpha} = $NORMAL_FORMAT{alpha};
181             }
182              
183 303         509 $self->{format} = $format;
184              
185 303         419 push @ver, map { $_ + 0 } @parts;
  491         927  
186              
187 303         526 $self->{version} = \@ver;
188              
189 303         883 return;
190             }
191              
192             sub _format {
193 264     264   366 my $self = shift;
194 264         330 my $format = shift;
195              
196 264         384 my @result = ();
197              
198 264         300 my @parts = @{ $self->{version} };
  264         602  
199 264         335 my @fmt = @{ $format->{printf} };
  264         626  
200              
201 264         835 push @parts, 0 while @parts < $format->{fields};
202              
203             # Adjust the format to be the same length as the number of fields
204 264         650 pop @fmt while @fmt > @parts;
205 264         931 push @fmt, $format->{extend} while @parts > @fmt;
206              
207             my $version
208             = ( $format->{prefix} )
209             . sprintf( join( '', @fmt ), @parts )
210 264         1181 . ( $format->{suffix} );
211              
212             $version .= sprintf( $format->{alpha}, $self->{alpha} )
213 264 100       667 if defined $self->{alpha};
214              
215 264         406 push @result, $version;
216              
217 264         1451 return join( ' ', @result );
218             }
219              
220             sub stringify {
221 137     137 1 92840 my $self = shift;
222 137   100     563 return $self->_format( $self->{format} || \%NORMAL_FORMAT );
223             }
224              
225             sub normal {
226 63     63 1 6386 return shift->_format( \%NORMAL_FORMAT );
227             }
228              
229             sub numify {
230 64     64 1 2961 return shift->_format( \%NUMERIC_FORMAT );
231             }
232              
233             sub is_alpha {
234 1     1 1 870 my $self = shift;
235 1         4 return exists $self->{alpha};
236             }
237              
238             sub vcmp {
239 423     423 1 3486 my ( $self, $other, $rev ) = @_;
240              
241             # Promote to object
242 423 100       899 $other = __PACKAGE__->new( $other ) unless ref $other;
243              
244 423 100 100     2860 croak "Can't compare with $other"
245             unless blessed $other && $other->isa( __PACKAGE__ );
246              
247 421 100       921 return $other->vcmp( $self, 0 ) if $rev;
248              
249 418         483 my @this = @{ $self->{version} };
  418         1050  
250 418         544 my @that = @{ $other->{version} };
  418         806  
251              
252 418         1250 push @this, 0 while @this < @that;
253 418         1047 push @that, 0 while @that < @this;
254              
255 418         864 while ( @this ) {
256 1159 100       3416 if ( my $cmp = ( shift( @this ) <=> shift( @that ) ) ) {
257 55         216 return $cmp;
258             }
259             }
260              
261 363   100     2620 return ( $self->{alpha} || 0 ) <=> ( $other->{alpha} || 0 );
      100        
262             }
263              
264             sub components {
265 392     392 1 1588 my $self = shift;
266              
267 392 100       713 if ( @_ ) {
268 9         15 my $fields = shift;
269              
270 9 100       24 if ( ref $fields eq 'ARRAY' ) {
271 3         10 $self->{version} = [@$fields];
272             }
273             else {
274 6 100       168 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         21  
  1         3  
279 4         9 push @{ $self->{version} }, 0,
280 5         9 while @{ $self->{version} } < $fields;
  9         40  
281             }
282             }
283             else {
284 383         443 return @{ $self->{version} };
  383         995  
285             }
286             }
287              
288             sub component {
289 387     387 1 5920 my $self = shift;
290 387         522 my $field = shift;
291              
292 387 100       869 defined $field or croak "You must specify a component number";
293              
294 386 100       814 if ( lc( $field ) eq 'alpha' ) {
295 95 100       180 if ( @_ ) {
296 34         47 my $alpha = shift;
297 34 100       70 if ( $alpha ) {
298 5         23 $self->{alpha} = $alpha;
299             }
300             else {
301 29         130 delete $self->{alpha};
302             }
303             }
304             else {
305 61   100     425 return $self->{alpha} || 0;
306             }
307             }
308             else {
309 291         571 $field = $self->_resolve_component_name( $field );
310 290         645 my $fields = $self->components;
311              
312 290 100       619 if ( @_ ) {
313 61 100       119 if ( $field >= $fields ) {
314              
315             # Extend array if necessary
316 2         7 $self->components( $field + 1 );
317             }
318              
319 61         176 $self->{version}->[$field] = shift;
320             }
321             else {
322 229 100 66     1118 return unless $field >= 0 && $field < $fields;
323 171         643 return $self->{version}->[$field];
324             }
325             }
326             }
327              
328             sub increment {
329 32     32 1 429 my $self = shift;
330 32         51 my $field = shift;
331 32         69 my $fields = $self->components;
332              
333 32 100       89 if ( lc( $field ) eq 'alpha' ) {
334 4         10 $self->alpha( $self->alpha + 1 );
335             }
336             else {
337 28         61 $field = $self->_resolve_component_name( $field );
338              
339 27 100 66     222 croak "Component $field is out of range 0..", $fields - 1
340             if $field < 0 || $field >= $fields;
341              
342             # Increment the field
343 26         59 $self->component( $field, $self->component( $field ) + 1 );
344              
345             # Zero out any following fields
346 26         70 while ( ++$field < $fields ) {
347 28         52 $self->component( $field, 0 );
348             }
349 26         52 $self->alpha( 0 );
350             }
351             }
352              
353             sub set {
354 2     2 1 3 my $self = shift;
355 2         5 my $other = shift;
356              
357 2 100       8 $other = __PACKAGE__->new( $other ) unless ref $other;
358              
359 2         6 my @comp = $other->components;
360              
361 2         6 $self->components( \@comp );
362 2         6 $self->alpha( $other->alpha );
363             }
364              
365             1;
366             __END__