| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
# Prefer numeric version for backwards compatibility |
|
2
|
6
|
|
|
6
|
|
708880
|
BEGIN { require 5.010000 }; ## no critic ( RequireUseStrict, RequireUseWarnings ) |
|
3
|
6
|
|
|
6
|
|
30
|
use strict; |
|
|
6
|
|
|
|
|
10
|
|
|
|
6
|
|
|
|
|
187
|
|
|
4
|
6
|
|
|
6
|
|
35
|
use warnings; |
|
|
6
|
|
|
|
|
14
|
|
|
|
6
|
|
|
|
|
499
|
|
|
5
|
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
package Version::Semantic; |
|
7
|
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
$Version::Semantic::VERSION = 'v1.3.0'; |
|
9
|
|
|
|
|
|
|
|
|
10
|
6
|
|
|
6
|
|
3479
|
use overload '<=>' => 'compare_to', '""' => 'to_string'; |
|
|
6
|
|
|
|
|
10411
|
|
|
|
6
|
|
|
|
|
41
|
|
|
11
|
|
|
|
|
|
|
|
|
12
|
6
|
|
|
6
|
|
3710
|
use PerlX::Maybe (); |
|
|
6
|
|
|
|
|
18552
|
|
|
|
6
|
|
|
|
|
3829
|
|
|
13
|
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
sub _croakf ( $@ ); |
|
15
|
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
my $prefix_re = qr/v/; |
|
17
|
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
# |
|
19
|
|
|
|
|
|
|
my $id_re = qr/(?: [0-9] | [a-zA-Z-] )+/x; |
|
20
|
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
# (ok) |
|
22
|
|
|
|
|
|
|
my $num_id_re = qr/0 | [1-9] | [1-9] [0-9]+/x; |
|
23
|
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
# |
|
25
|
|
|
|
|
|
|
my $alnum_id_re = qr/[a-zA-Z-] | [a-zA-Z-] $id_re | $id_re [a-zA-Z-] | $id_re [a-zA-Z-] $id_re/x; |
|
26
|
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
# |
|
28
|
|
|
|
|
|
|
#my $build_id_re = qr/[0-9a-zA-Z-]+/; |
|
29
|
|
|
|
|
|
|
my $build_id_re = qr/$alnum_id_re | [0-9]+/x; |
|
30
|
|
|
|
|
|
|
# (ok) |
|
31
|
|
|
|
|
|
|
my $build_re = qr/$build_id_re (?: \. $build_id_re )*/x; |
|
32
|
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
# |
|
34
|
|
|
|
|
|
|
#my $pre_release_id_re = qr/$num_id_re | [0-9]* [a-zA-Z-] [0-9a-zA-Z-]*/x; |
|
35
|
|
|
|
|
|
|
my $pre_release_id_re = qr/$num_id_re | $alnum_id_re/x; |
|
36
|
|
|
|
|
|
|
# (ok) |
|
37
|
|
|
|
|
|
|
my $pre_release_re = qr/$pre_release_id_re (?: \. $pre_release_id_re )*/x; |
|
38
|
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
# Use BNF terminology |
|
40
|
|
|
|
|
|
|
# https://semver.org/spec/v2.0.0.html#backusnaur-form-grammar-for-valid-semver-versions |
|
41
|
9
|
|
|
9
|
0
|
48
|
sub prefix { shift->{ prefix } } |
|
42
|
87
|
|
|
87
|
0
|
356
|
sub major { shift->{ major } } |
|
43
|
77
|
|
|
77
|
0
|
279
|
sub minor { shift->{ minor } } |
|
44
|
80
|
|
|
80
|
0
|
348
|
sub patch { shift->{ patch } } |
|
45
|
20
|
|
|
20
|
0
|
87
|
sub version_core { shift->{ version_core } } |
|
46
|
63
|
|
|
63
|
0
|
311
|
sub pre_release { shift->{ pre_release } } |
|
47
|
4
|
|
|
4
|
0
|
20
|
sub build { shift->{ build } } |
|
48
|
|
|
|
|
|
|
|
|
49
|
4
|
|
|
4
|
0
|
52
|
sub has_prefix { defined shift->{ prefix } } |
|
50
|
59
|
|
|
59
|
0
|
254
|
sub has_pre_release { defined shift->{ pre_release } } |
|
51
|
13
|
|
|
13
|
0
|
94
|
sub has_build { defined shift->{ build } } |
|
52
|
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
{ |
|
54
|
|
|
|
|
|
|
## no critic ( ProhibitComplexRegexes ) |
|
55
|
|
|
|
|
|
|
# On purpose use "build" (the BNF symbol name) instead of "buildmetadata" as |
|
56
|
|
|
|
|
|
|
# the name of the last named capture group |
|
57
|
|
|
|
|
|
|
# (ok) |
|
58
|
|
|
|
|
|
|
my $semver_re = qr/ |
|
59
|
|
|
|
|
|
|
(? $prefix_re)? |
|
60
|
|
|
|
|
|
|
(? $num_id_re) \. (? $num_id_re) \. (? $num_id_re) |
|
61
|
|
|
|
|
|
|
(?: - (? $pre_release_re) )? |
|
62
|
|
|
|
|
|
|
(?: \+ (? $build_re) )? |
|
63
|
|
|
|
|
|
|
/x; |
|
64
|
1
|
|
|
1
|
0
|
205890
|
sub semver_re { $semver_re } |
|
65
|
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
# Constructor as factory method |
|
67
|
|
|
|
|
|
|
sub parse { |
|
68
|
152
|
|
|
152
|
0
|
569036
|
my ( $class, $version ) = @_; |
|
69
|
152
|
|
100
|
|
|
404
|
$version //= ''; |
|
70
|
|
|
|
|
|
|
|
|
71
|
152
|
100
|
|
|
|
4664
|
$version =~ m/\A$semver_re\z/ |
|
72
|
|
|
|
|
|
|
or _croakf "Version '%s' is not a semantic version", $version; |
|
73
|
|
|
|
|
|
|
|
|
74
|
110
|
|
|
|
|
1422
|
$class->new( %+ ) |
|
75
|
|
|
|
|
|
|
} |
|
76
|
|
|
|
|
|
|
} |
|
77
|
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
{ |
|
79
|
|
|
|
|
|
|
my %attrs = ( |
|
80
|
|
|
|
|
|
|
prefix => $prefix_re, |
|
81
|
|
|
|
|
|
|
major => $num_id_re, |
|
82
|
|
|
|
|
|
|
minor => $num_id_re, |
|
83
|
|
|
|
|
|
|
patch => $num_id_re, |
|
84
|
|
|
|
|
|
|
pre_release => $pre_release_re, |
|
85
|
|
|
|
|
|
|
build => $build_re |
|
86
|
|
|
|
|
|
|
); |
|
87
|
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
sub new { |
|
89
|
124
|
|
|
124
|
0
|
129996
|
my $invocant = shift; |
|
90
|
124
|
|
|
|
|
177
|
my %args; |
|
91
|
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
# Validate args |
|
93
|
|
|
|
|
|
|
{ |
|
94
|
6
|
|
|
6
|
|
69
|
use warnings FATAL => qw( misc uninitialized ); |
|
|
6
|
|
|
|
|
12
|
|
|
|
6
|
|
|
|
|
7833
|
|
|
|
124
|
|
|
|
|
178
|
|
|
95
|
124
|
|
|
|
|
1011
|
%args = @_ |
|
96
|
|
|
|
|
|
|
}; |
|
97
|
122
|
|
|
|
|
380
|
foreach ( keys %args ) { |
|
98
|
442
|
50
|
|
|
|
1054
|
unless ( defined $args{ $_ } ) { |
|
99
|
0
|
|
|
|
|
0
|
delete $args{ $_ }; |
|
100
|
|
|
|
|
|
|
next |
|
101
|
0
|
|
|
|
|
0
|
} |
|
102
|
|
|
|
|
|
|
_croakf "Unknown attribute name '%s'", $_ |
|
103
|
442
|
100
|
|
|
|
811
|
unless exists $attrs{ $_ }; |
|
104
|
|
|
|
|
|
|
_croakf "Attribute '%s' has invalid value '%s'", $_, $args{ $_ } |
|
105
|
441
|
100
|
|
|
|
26736
|
unless $args{ $_ } =~ m/\A $attrs{ $_ } \z/x |
|
106
|
|
|
|
|
|
|
} |
|
107
|
|
|
|
|
|
|
|
|
108
|
120
|
|
|
|
|
263
|
my $class; |
|
109
|
120
|
100
|
|
|
|
377
|
if ( $class = ref $invocant ) { |
|
110
|
|
|
|
|
|
|
# Shallow copy |
|
111
|
8
|
|
|
|
|
60
|
%args = ( %$invocant, %args ) |
|
112
|
|
|
|
|
|
|
} else { |
|
113
|
112
|
|
|
|
|
216
|
$class = $invocant; |
|
114
|
|
|
|
|
|
|
} |
|
115
|
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
# Don't move this attribute checking |
|
117
|
|
|
|
|
|
|
exists $args{ $_ } or _croakf "Required attribute '%s' not set", $_ |
|
118
|
120
|
|
66
|
|
|
577
|
foreach qw( major minor patch ); |
|
119
|
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
bless { %args, |
|
121
|
119
|
|
100
|
|
|
630
|
version_core => ( $args{ prefix } // '' ) . join( '.', map { $args{ $_ } } qw( major minor patch ) ) } => $class |
|
|
357
|
|
|
|
|
1867
|
|
|
122
|
|
|
|
|
|
|
} |
|
123
|
|
|
|
|
|
|
} |
|
124
|
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
{ |
|
126
|
|
|
|
|
|
|
my $trial_pre_release = qr/\A ( TRIAL ) ( [0-9]* ) \z/x; |
|
127
|
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
sub increment { |
|
129
|
|
|
|
|
|
|
# Obvious strategies are major|minor|patch |
|
130
|
11
|
|
|
11
|
0
|
68
|
my ( $self, $strategy, $pre_release ) = @_; |
|
131
|
11
|
|
100
|
|
|
31
|
$strategy //= 'patch'; |
|
132
|
|
|
|
|
|
|
|
|
133
|
11
|
100
|
|
|
|
26
|
if ( $strategy eq 'trial' ) { |
|
134
|
4
|
100
|
|
|
|
9
|
if ( $self->has_pre_release ) { |
|
135
|
3
|
100
|
|
|
|
5
|
if ( my ( $string, $number ) = $self->pre_release =~ $trial_pre_release ) { |
|
136
|
2
|
100
|
|
|
|
9
|
return $self->new( pre_release => $string . ( ( $number eq '' ? 0 : $number ) + 1 ) ) |
|
137
|
|
|
|
|
|
|
} else { |
|
138
|
1
|
|
|
|
|
3
|
_croakf "Pre-release extension '%s' does not match '%s'", $self->pre_release, $trial_pre_release |
|
139
|
|
|
|
|
|
|
} |
|
140
|
|
|
|
|
|
|
} else { |
|
141
|
1
|
|
|
|
|
4
|
_croakf "Cannot apply '%s' version incrementation strategy to non pre-release version '%s'", $strategy, $self |
|
142
|
|
|
|
|
|
|
} |
|
143
|
|
|
|
|
|
|
} |
|
144
|
7
|
100
|
|
|
|
26
|
return $self->new( patch => $self->patch + 1, PerlX::Maybe::maybe pre_release => $pre_release ) |
|
145
|
|
|
|
|
|
|
if $strategy eq 'patch'; |
|
146
|
3
|
100
|
|
|
|
13
|
return $self->new( minor => $self->minor + 1, patch => 0, PerlX::Maybe::maybe pre_release => $pre_release ) |
|
147
|
|
|
|
|
|
|
if $strategy eq 'minor'; |
|
148
|
2
|
100
|
|
|
|
9
|
return $self->new( major => $self->major + 1, minor => 0, patch => 0, PerlX::Maybe::maybe pre_release => $pre_release ) |
|
149
|
|
|
|
|
|
|
if $strategy eq 'major'; |
|
150
|
|
|
|
|
|
|
|
|
151
|
1
|
|
|
|
|
4
|
_croakf "Version incrementation strategy '%s' is not implemented", $strategy |
|
152
|
|
|
|
|
|
|
} |
|
153
|
|
|
|
|
|
|
} |
|
154
|
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
# https://semver.org/spec/v2.0.0.html#spec-item-11 |
|
156
|
|
|
|
|
|
|
sub compare_to { |
|
157
|
33
|
|
|
33
|
0
|
1476
|
my ( $self, $other ) = @_; |
|
158
|
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
# 11.2 |
|
160
|
33
|
|
|
|
|
84
|
for ( qw( major minor patch ) ) { |
|
161
|
88
|
100
|
|
|
|
335
|
return $self->$_ <=> $other->$_ if $self->$_ != $other->$_ |
|
162
|
|
|
|
|
|
|
} |
|
163
|
20
|
|
|
|
|
56
|
$self->_compare_pre_release( $other ) |
|
164
|
|
|
|
|
|
|
} |
|
165
|
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
sub to_string { |
|
167
|
8
|
|
|
8
|
0
|
21
|
my ( $self ) = @_; |
|
168
|
|
|
|
|
|
|
|
|
169
|
8
|
|
|
|
|
27
|
my $string = $self->version_core; |
|
170
|
8
|
100
|
|
|
|
19
|
$string .= '-' . $self->pre_release if $self->has_pre_release; |
|
171
|
8
|
100
|
|
|
|
18
|
$string .= '+' . $self->build if $self->has_build; |
|
172
|
8
|
|
|
|
|
49
|
$string |
|
173
|
|
|
|
|
|
|
} |
|
174
|
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
sub _compare_pre_release { |
|
176
|
20
|
|
|
20
|
|
44
|
my ( $self, $other ) = @_; |
|
177
|
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
# Split pre-release into list of dot separated identifiers |
|
179
|
20
|
100
|
|
|
|
46
|
my @a = $self->has_pre_release ? split /\./, $self->pre_release : (); |
|
180
|
20
|
100
|
|
|
|
50
|
my @b = $other->has_pre_release ? split /\./, $other->pre_release : (); |
|
181
|
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
# 11.3 |
|
183
|
20
|
100
|
|
|
|
51
|
if ( @a ) { |
|
184
|
18
|
100
|
|
|
|
52
|
return -1 if not @b |
|
185
|
|
|
|
|
|
|
} else { |
|
186
|
2
|
100
|
|
|
|
14
|
return ( @b ? 1 : 0 ) |
|
187
|
|
|
|
|
|
|
} |
|
188
|
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
# 11.4 |
|
190
|
16
|
100
|
|
|
|
45
|
my $len = @a < @b ? @a : @b; |
|
191
|
16
|
|
|
|
|
48
|
for ( my $i = 0 ; $i < $len ; $i++ ) { |
|
192
|
22
|
|
|
|
|
47
|
my $ai = $a[ $i ]; |
|
193
|
22
|
|
|
|
|
34
|
my $bi = $b[ $i ]; |
|
194
|
|
|
|
|
|
|
|
|
195
|
22
|
|
|
|
|
253
|
my $ai_is_num = $ai =~ m/\A $num_id_re \z/x; |
|
196
|
22
|
|
|
|
|
141
|
my $bi_is_num = $bi =~ m/\A $num_id_re \z/x; |
|
197
|
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
# 11.4.1 |
|
199
|
22
|
100
|
100
|
|
|
142
|
if ( $ai_is_num and $bi_is_num ) { |
|
|
|
100
|
66
|
|
|
|
|
|
|
|
100
|
66
|
|
|
|
|
|
200
|
5
|
|
|
|
|
14
|
my $sign = $ai <=> $bi; |
|
201
|
5
|
100
|
|
|
|
41
|
return $sign if $sign != 0 |
|
202
|
|
|
|
|
|
|
# 11.4.3 |
|
203
|
|
|
|
|
|
|
} elsif ( $ai_is_num and not $bi_is_num ) { |
|
204
|
1
|
|
|
|
|
11
|
return -1 |
|
205
|
|
|
|
|
|
|
# 11.4.3 |
|
206
|
|
|
|
|
|
|
} elsif ( not $ai_is_num and $bi_is_num ) { |
|
207
|
1
|
|
|
|
|
9
|
return 1 |
|
208
|
|
|
|
|
|
|
} else { |
|
209
|
15
|
|
|
|
|
32
|
my $sign = $ai cmp $bi; |
|
210
|
15
|
100
|
|
|
|
87
|
return $sign if $sign != 0 |
|
211
|
|
|
|
|
|
|
} |
|
212
|
|
|
|
|
|
|
} |
|
213
|
|
|
|
|
|
|
|
|
214
|
|
|
|
|
|
|
# 11.4.4 |
|
215
|
5
|
|
|
|
|
45
|
@a <=> @b |
|
216
|
|
|
|
|
|
|
} |
|
217
|
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
sub _croakf ( $@ ) { |
|
219
|
48
|
|
|
48
|
|
297
|
require Carp; |
|
220
|
48
|
50
|
|
|
|
277
|
@_ = ( ( @_ == 1 ? shift : sprintf shift, @_ ) . ', stopped' ); |
|
221
|
48
|
|
|
|
|
4049
|
goto &Carp::croak |
|
222
|
|
|
|
|
|
|
} |
|
223
|
|
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
1 |