SemVer.pm (17384B)
1 package SemVer; 2 3 use 5.008001; 4 use strict; 5 use version 0.82; 6 use Scalar::Util (); 7 8 use overload ( 9 '""' => 'stringify', 10 '<=>' => 'vcmp', 11 'cmp' => 'vcmp', 12 'bool' => 'vbool', 13 ); 14 15 our @ISA = qw(version); 16 our $VERSION = '0.10.1'; # For Module::Build 17 18 sub _die { require Carp; Carp::croak(@_) } 19 20 # Prevent version.pm from mucking with our internals. 21 sub import {} 22 23 sub new { 24 my ($class, $ival) = @_; 25 26 # Handle vstring. 27 return $class->SUPER::new($ival) if Scalar::Util::isvstring($ival); 28 29 # Let version handle cloning. 30 if (eval { $ival->isa('version') }) { 31 my $self = $class->SUPER::new($ival); 32 $self->{extra} = $ival->{extra}; 33 $self->{patch} = $ival->{patch}; 34 $self->{prerelease} = $ival->{prerelease}; 35 return $self; 36 } 37 38 # Regex taken from https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string. 39 my ($major, $minor, $patch, $prerelease, $meta) = ( 40 $ival =~ /^v?(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/ 41 ); 42 _die qq{Invalid semantic version string format: "$ival"} 43 unless defined $major; 44 45 return _init($class->SUPER::new("$major.$minor.$patch"), $prerelease, $meta); 46 } 47 48 sub _init { 49 my ($self, $pre, $meta) = @_; 50 if (defined $pre) { 51 $self->{extra} = "-$pre"; 52 @{$self->{prerelease}} = split /[.]/, $pre; 53 } 54 if (defined $meta) { 55 $self->{extra} .= "+$meta"; 56 @{$self->{patch}} = split /[.]/, $meta; 57 } 58 59 return $self; 60 } 61 62 $VERSION = __PACKAGE__->new($VERSION); # For ourselves. 63 64 sub _lenient { 65 my ($class, $ctor, $ival) = @_; 66 return $class->new($ival) if Scalar::Util::isvstring($ival) 67 or eval { $ival->isa('version') }; 68 69 # Use official regex for prerelease and meta, use more lenient version num matching and whitespace. 70 my ($v, $prerelease, $meta) = ( 71 $ival =~ /^[[:space:]]* 72 v?([\d_]+(?:\.[\d_]+(?:\.[\d_]+)?)?) 73 (?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))? 74 [[:space:]]*$/x 75 ); 76 77 _die qq{Invalid semantic version string format: "$ival"} 78 unless defined $v; 79 80 $v += 0 if $v && $v =~ s/_//g; # ignore underscores. 81 my $code = $class->can("SUPER::$ctor"); 82 return _init($code->($class, $v), $prerelease, $meta); 83 } 84 85 sub declare { 86 shift->_lenient('declare', @_); 87 } 88 89 sub parse { 90 shift->_lenient('parse', @_); 91 } 92 93 sub stringify { 94 my $self = shift; 95 my $str = $self->SUPER::stringify; 96 # This is purely for SemVers constructed from version objects. 97 $str += 0 if $str =~ s/_//g; # ignore underscores. 98 return $str . ($self->{dash} || '') . ($self->{extra} || ''); 99 } 100 101 sub normal { 102 my $self = shift; 103 (my $norm = $self->SUPER::normal) =~ s/^v//; 104 $norm =~ s/_/./g; 105 return $norm . ($self->{extra} || ''); 106 } 107 108 sub numify { _die 'Semantic versions cannot be numified'; } 109 sub is_alpha { !!shift->{extra} } 110 sub vbool { 111 my $self = shift; 112 return version::vcmp($self, $self->new("0.0.0"), 1); 113 } 114 115 # Sort Ordering: 116 # Precedence refers to how versions are compared to each other when ordered. Precedence MUST be calculated by 117 # separating the version into major, minor, patch and pre-release identifiers in that order (Build metadata does not figure into precedence). 118 # Precedence is determined by the first difference when comparing each of these identifiers from left to right as follows: 119 # 1. Major, minor, and patch versions are always compared numerically. Example: 1.0.0 < 2.0.0 < 2.1.0 < 2.1.1. 120 # 2. When major, minor, and patch are equal, a pre-release version has lower precedence than a normal version. 121 # Example: 1.0.0-alpha < 1.0.0. 122 # 3. Precedence for two pre-release versions with the same major, minor, and patch version MUST be determined by 123 # comparing each dot separated identifier from left to right until a difference is found as follows: 124 # 3.a. identifiers consisting of only digits are compared numerically and identifiers with letters or hyphens are 125 # compared lexically in ASCII sort order. 126 # 3.b. Numeric identifiers always have lower precedence than non-numeric identifiers. 127 # 3.c. A larger set of pre-release fields has a higher precedence than a smaller set, if all of the preceding identifiers are equal. 128 # Example: 1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-alpha.beta < 1.0.0-beta < 1.0.0-beta.2 < 1.0.0-beta.11 < 1.0.0-rc.1 < 1.0.0. 129 sub vcmp { 130 my $left = shift; 131 my $right = ref($left)->declare(shift); 132 133 # Reverse? 134 ($left, $right) = shift() ? ($right, $left): ($left, $right); 135 136 # Major and minor win. - case 1. 137 if (my $ret = $left->SUPER::vcmp($right, 0)) { 138 return $ret; 139 } else { #cases 2, 3 140 my $lenLeft = 0; 141 my $lenRight = 0; 142 if (defined $left->{prerelease}) { 143 $lenLeft = scalar(@{$left->{prerelease}}); 144 } 145 if (defined $right->{prerelease}) { 146 $lenRight = scalar(@{$right->{prerelease}}); 147 } 148 my $lenMin = ($lenLeft, $lenRight)[$lenLeft > $lenRight]; 149 if ( $lenLeft == 0) { 150 if ($lenRight == 0) { 151 return 0; # Neither LEFT nor RIGHT have prerelease identifiers - versions are equal 152 } else { 153 # Case 2: When major, minor, and patch are equal, a pre-release version has lower precedence than a normal version. 154 return 1; # Only RIGHT has prelease - not LEFT -> LEFT wins 155 } 156 } else { 157 if ($lenRight == 0) { 158 # Case 2: When major, minor, and patch are equal, a pre-release version has lower precedence than a normal version. 159 return -1; # Only LEFT has prelease identifiers - not RIGHT -> RIGHT wins 160 } else { 161 # LEFT and RIGHT have prelease identifiers - compare each part separately 162 for (my $i = 0; $i < $lenMin; $i++) { 163 my $isNumLeft = Scalar::Util::looks_like_number($left->{prerelease}->[$i]); 164 my $isNumRight = Scalar::Util::looks_like_number($right->{prerelease}->[$i]); 165 # Case 3.b: Numeric identifiers always have lower precedence than non-numeric identifiers 166 if (!$isNumLeft && $isNumRight) { 167 return 1; # LEFT identifier is Non-numeric - RIGHT identifier is numeric -> LEFT wins 168 } elsif ($isNumLeft && !$isNumRight) { 169 return -1; # LEFT identifier is numeric - RIGHT identifier is non-numeric -> RIGHT wins 170 } elsif ($isNumLeft && $isNumRight) { 171 # Case 3.a.1: identifiers consisting of only digits are compared numerically 172 if ($left->{prerelease}->[$i] == $right->{prerelease}->[$i] ) { 173 next; # LEFT identifier and RIGHT identifier are equal - step to next part 174 } elsif ($left->{prerelease}->[$i] > $right->{prerelease}->[$i] ) { 175 return 1; # LEFT identifier is bigger than RIGHT identifier -> LEFT wins 176 } else { 177 return -1; return 1; # LEFT identifier is smaller than RIGHT identifier -> RIGHT wins 178 } 179 } else { 180 # Case 3.a.2: identifiers with letters or hyphens are compared lexically in ASCII sort order. 181 if (lc $left->{prerelease}->[$i] eq lc $right->{prerelease}->[$i] ) { 182 next; # LEFT identifier and RIGHT identifier are equal - step to next part 183 } elsif (lc $left->{prerelease}->[$i] gt lc $right->{prerelease}->[$i] ) { 184 return 1; # LEFT identifier is bigger than RIGHT identifier -> LEFT wins 185 } else { 186 return -1; return 1; # LEFT identifier is smaller than RIGHT identifier -> RIGHT wins 187 } 188 } 189 } 190 # Case 3.c: A larger set of pre-release fields has a higher precedence than a smaller set, if all of the preceding identifiers are equal 191 if ($lenLeft > $lenRight) { 192 return 1; # All existing identifiers are equal, but LEFT has more identifiers -> LEFT wins 193 } elsif ($lenLeft < $lenRight) { 194 return -1; # All existing identifiers are equal, but RIGHT has more identifiers -> RIGHT wins 195 } 196 # All identifiers are equal 197 return 0; 198 } 199 } 200 } 201 } 202 203 1; 204 __END__ 205 206 =head1 Name 207 208 SemVer - Use semantic version numbers 209 210 =head1 Synopsis 211 212 use SemVer; our $VERSION = SemVer->new('1.2.0-b1'); 213 214 =head1 Description 215 216 This module subclasses L<version> to create semantic versions, as defined by 217 the L<Semantic Versioning 2.0.0 Specification|https://semver.org/spec/v2.0.0.html>. 218 The three salient points of the specification, for the purposes of version 219 formatting, are: 220 221 =over 222 223 =item 1. 224 225 A normal version number MUST take the form X.Y.Z where X, Y, and Z are non-negative 226 integers, and MUST NOT contain leading zeroes. X is the major version, Y is the 227 minor version, and Z is the patch version. Each element MUST increase numerically. 228 For instance: C<< 1.9.0 -> 1.10.0 -> 1.11.0 >>. 229 230 =item 2. 231 232 A pre-release version MAY be denoted by appending a hyphen and a series of dot 233 separated identifiers immediately following the patch version. Identifiers MUST 234 comprise only ASCII alphanumerics and hyphen C<[0-9A-Za-z-]>. Identifiers MUST NOT 235 be empty. Numeric identifiers MUST NOT include leading zeroes. Pre-release versions 236 have a lower precedence than the associated normal version. A pre-release version 237 indicates that the version is unstable and might not satisfy the intended 238 compatibility requirements as denoted by its associated normal version: 239 C<< 1.0.0-alpha, 1.0.0-alpha.1, 1.0.0-0.3.7, 1.0.0-x.7.z.92 >> 240 241 =item 3. 242 243 Build metadata MAY be denoted by appending a plus sign and a series of dot separated 244 identifiers immediately following the patch or pre-release version. Identifiers MUST 245 comprise only ASCII alphanumerics and hyphen C<[0-9A-Za-z-]>. Identifiers MUST NOT 246 be empty. Build metadata SHOULD be ignored when determining version precedence. Thus 247 two versions that differ only in the build metadata, have the same precedence. 248 Examples: C<< 1.0.0-alpha+001, 1.0.0+20130313144700, 1.0.0-beta+exp.sha.5114f85 >>. 249 250 =back 251 252 =head2 Usage 253 254 For strict parsing of semantic version numbers, use the C<new()> constructor. 255 If you need something more flexible, use C<declare()>. And if you need 256 something more comparable with what L<version> expects, try C<parse()>. 257 Compare how these constructors deal with various version strings (with values 258 shown as returned by C<normal()>: 259 260 Argument | new | declare | parse 261 -------------+----------+--------------------------- 262 '1.0.0' | 1.0.0 | 1.0.0 | 1.0.0 263 '5.5.2-b1' | 5.5.2-b1 | 5.5.2-b1 | 5.5.2-b1 264 '1.05.0' | <error> | 1.5.0 | 1.5.0 265 '1.0' | <error> | 1.0.0 | 1.0.0 266 ' 012.2.2' | <error> | 12.2.2 | 12.2.2 267 '1.1' | <error> | 1.1.0 | 1.100.0 268 1.1 | <error> | 1.1.0 | 1.100.0 269 '1.1.0+b1' | 1.1.0+b1 | 1.1.0+b1 | 1.1.0+b1 270 '1.1-b1' | <error> | 1.1.0-b1 | 1.100.0-b1 271 '1.2.b1' | <error> | 1.2.0-b1 | 1.2.0-b1 272 '9.0-beta4' | <error> | 9.0.0-beta4 | 9.0.0-beta4 273 '9' | <error> | 9.0.0 | 9.0.0 274 '1-b' | <error> | 1.0.0-b | 1.0.0-b 275 0 | <error> | 0.0.0 | 0.0.0 276 '0-rc1' | <error> | 0.0.0-rc1 | 0.0.0-rc1 277 '1.02_30' | <error> | 1.23.0 | 1.23.0 278 1.02_30 | <error> | 1.23.0 | 1.23.0 279 280 Note that, unlike in L<version>, the C<declare> and C<parse> methods ignore 281 underscores. That is, version strings with underscores are treated as decimal 282 numbers. Hence, the last two examples yield exactly the same semantic 283 versions. 284 285 As with L<version> objects, the comparison and stringification operators are 286 all overloaded, so that you can compare semantic versions. You can also 287 compare semantic versions with version objects (but not the other way around, 288 alas). Boolean operators are also overloaded, such that all semantic version 289 objects except for those consisting only of zeros (ignoring prerelease and 290 metadata) are considered true. 291 292 =head1 Interface 293 294 =head2 Constructors 295 296 =head3 C<new> 297 298 my $semver = SemVer->new('1.2.2'); 299 300 Performs a validating parse of the version string and returns a new semantic 301 version object. If the version string does not adhere to the semantic version 302 specification an exception will be thrown. See C<declare> and C<parse> for 303 more forgiving constructors. 304 305 =head3 C<declare> 306 307 my $semver = SemVer->declare('1.2'); # 1.2.0 308 309 This parser strips out any underscores from the version string and passes it 310 to to C<version>'s C<declare> constructor, which always creates dotted-integer 311 version objects. This is the most flexible way to declare versions. Consider 312 using it to normalize version strings. 313 314 =head3 C<parse> 315 316 my $semver = SemVer->parse('1.2'); # 1.200.0 317 318 This parser dispatches to C<version>'s C<parse> constructor, which tries to be 319 more flexible in how it converts simple decimal strings and numbers. Not 320 really recommended, since it's treatment of decimals is quite different from 321 the dotted-integer format of semantic version strings, and thus can lead to 322 inconsistencies. Included only for proper compatibility with L<version>. 323 324 =head2 Instance Methods 325 326 =head3 C<normal> 327 328 SemVer->declare('v1.2')->normal; # 1.2.0 329 SemVer->parse('1.2')->normal; # 1.200.0 330 SemVer->declare('1.02.0-b1')->normal; # 1.2.0-b1 331 SemVer->parse('1.02_30')->normal # 1.230.0 332 SemVer->parse(1.02_30)->normal # 1.23.0 333 334 Returns a normalized representation of the version string. This string will 335 always be a strictly-valid dotted-integer semantic version string suitable 336 for passing to C<new()>. Unlike L<version>'s C<normal> method, there will be 337 no leading "v". 338 339 =head3 C<stringify> 340 341 SemVer->declare('v1.2')->stringify; # v1.2 342 SemVer->parse('1.200')->stringify; # v1.200 343 SemVer->declare('1.2-r1')->stringify; # v1.2-r1 344 SemVer->parse(1.02_30)->stringify; # v1.0230 345 SemVer->parse(1.02_30)->stringify; # v1.023 346 347 Returns a string that is as close to the original representation as possible. 348 If the original representation was a numeric literal, it will be returned the 349 way perl would normally represent it in a string. This method is used whenever 350 a version object is interpolated into a string. 351 352 =head3 C<numify> 353 354 Throws an exception. Semantic versions cannot be numified. Just don't go 355 there. 356 357 =head3 C<is_alpha> 358 359 my $is_alpha = $semver->is_alpha; 360 361 Returns true if a prerelease and/or metadata string is appended to the end of 362 the version string. This also means that the version number is a "special 363 version", in the semantic versioning specification meaning of the phrase. 364 365 =head3 C<vbool> 366 367 say "Version $semver" if $semver; 368 say "Not a $semver" if !$semver; 369 370 Returns true for a non-zero semantic semantic version object, without regard 371 to the prerelease or build metadata parts. Overloads boolean operations. 372 373 =head3 C<vcmp> 374 375 Compares the semantic version object to another version object or string and 376 returns 0 if they're the same, -1 if the invocant is smaller than the 377 argument, and 1 if the invocant is greater than the argument. 378 379 Mostly you don't need to worry about this: Just use the comparison operators 380 instead: 381 382 if ($semver < $another_semver) { 383 die "Need $another_semver or higher"; 384 } 385 386 Note that in addition to comparing other semantic version objects, you can 387 also compare regular L<version> objects: 388 389 if ($semver < $version) { 390 die "Need $version or higher"; 391 } 392 393 You can also pass in a version string. It will be turned into a semantic 394 version object using C<declare>. So if you're using numeric versions, you may 395 or may not get what you want: 396 397 my $semver = version::Semver->new('1.2.0'); 398 my $version = '1.2'; 399 my $bool = $semver == $version; # true 400 401 If that's not what you want, pass the string to C<parse> first: 402 403 my $semver = Semver->new('1.2.0'); 404 my $version = Semver->parse('1.2'); # 1.200.0 405 my $bool = $semver == $version; # false 406 407 =head1 See Also 408 409 =over 410 411 =item * L<Semantic Versioning Specification|https://semver.org/>. 412 413 =item * L<version> 414 415 =item * L<version::AlphaBeta> 416 417 =back 418 419 =head1 Support 420 421 This module is managed in an open 422 L<GitHub repository|https://github.com/theory/semver/>. Feel free to fork and 423 contribute, or to clone L<https://github.com/theory/semver.git> and send 424 patches! 425 426 Found a bug? Please L<post|https://github.com/theory/semver/issues> a report! 427 428 =head1 Acknowledgements 429 430 Many thanks to L<version> author John Peacock for his suggestions and 431 debugging help. 432 433 =head1 Authors 434 435 =over 436 437 =item * David E. Wheeler <david@kineticode.com> 438 439 =item * Johannes Kilian <hoppfrosch@gmx.de> 440 441 =back 442 443 =head1 Copyright and License 444 445 Copyright (c) 2010-2020 David E. Wheeler. Some Rights Reserved. 446 447 This module is free software; you can redistribute it and/or modify it under 448 the same terms as Perl itself. 449 450 =cut