bluto

Release package and announcement generator
Info | Log | Files | Refs | README | LICENSE

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