File Coverage

blib/lib/Pandoc/Version.pm
Criterion Covered Total %
statement 60 60 100.0
branch 15 16 93.7
condition 9 12 75.0
subroutine 13 13 100.0
pod 6 7 85.7
total 103 108 95.3


line stmt bran cond sub pod time code
1             package Pandoc::Version;
2 11     11   162102 use 5.014;
  11         77  
3 11     11   74 use warnings;
  11         23  
  11         1206  
4              
5             our $VERSION = '0.9.2';
6              
7             use overload
8 11         78 '""' => 'string',
9             '0+' => 'number',
10             cmp => 'cmp',
11             '<=>' => 'cmp',
12 11     11   7097 fallback => 1;
  11         27080  
13 11     11   2026 use Scalar::Util qw(reftype blessed);
  11         25  
  11         930  
14 11     11   5635 use Pandoc::Error;
  11         35  
  11         4527  
15              
16             our @CARP_NOT = ('Pandoc');
17              
18             sub new {
19 87     87 0 258191 my $class = shift;
20              
21             # We accept array or string input
22             # (or mixed but let's not document that!)
23             my @nums =
24             map {
25 212         359 my $num = $_;
26 212 100       712 $num =~ /^\d+$/
27             or Pandoc::Error->throw(
28             message => 'invalid version number',
29             version => $num
30             );
31 207         368 $num =~ s/^0+(?=\d)//; # ensure decimal interpretation
32 207         365 $num = 0 + $num;
33 207         450 $num
34             }
35 109         289 map { s/^v//i; split /\./ } ## no critic
  109         329  
36 89 100 100     425 map { 'ARRAY' CORE::eq ( reftype $_ // "" ) ? @$_ : $_ }
37 87   100     188 map { $_ // '' } @_;
  89         1601  
38              
39 82 100       268 Pandoc::Error->throw('invalid version number') unless @nums;
40              
41 79         258 return bless \@nums => $class;
42             }
43              
44 25     25 1 1758 sub string { join '.', @{ $_[0] } }
  25         161  
45              
46             sub number {
47 125     125 1 179 my ( $major, @minors ) = @{ $_[0] };
  125         342  
48 11     11   96 no warnings qw(uninitialized numeric);
  11         24  
  11         9705  
49 125 100       294 if (@minors) {
50 113         199 my $minor = join '', map { sprintf '%03d', $_ } @minors;
  161         521  
51 113         702 return 0 + "$major.$minor"; # return a true number
52             }
53 12         43 return 0 + $major;
54             }
55              
56             sub cmp {
57             my ( $a, $b ) = map {
58 55 100 66 55 1 137 ( blessed $_ and $_->isa('Pandoc::Version') )
  110   33     600  
59             ? $_
60             : Pandoc::Version->new( $_ // () )
61             } ( $_[0], $_[1] );
62 55         143 return $a->number <=> $b->number;
63             }
64              
65             sub match {
66 7     7 1 2342 my ( $a, $b ) = map { Pandoc::Version->new($_) } @_;
  14         40  
67 7         30 pop @$a while @$a > @$b;
68 7         21 pop @$b while @$b > @$a;
69              
70 7         19 return $a->number == $b->number;
71             }
72              
73             my %cmp_truth_table = (
74             '==' => [ 0, 1, 0 ],
75             '!=' => [ 1, 0, 1 ],
76             '>=' => [ 0, 1, 1 ],
77             '<=' => [ 1, 1, 0 ],
78             '<' => [ 1, 0, 0 ],
79             '>' => [ 0, 0, 1 ]
80             );
81              
82             sub fulfills {
83 17     17 1 6150 my ( $self, $req ) = @_;
84 17 50       73 return 1 unless $req;
85              
86 17         170 my @parts = split qr{\s*,\s*}, $req;
87 17         51 for my $part (@parts) {
88 32         238 my ( $op, $ver ) =
89             $part =~ m{^\s*(==|>=|>|<=|<|!=)?\s*v?(\d+(\.\d+)*)$};
90 32 100       85 if ( !defined $ver ) {
91 1         8 Pandoc::Error->throw(
92             message => "invalid version requirement: $req",
93             require => $req,
94             );
95             }
96              
97 31         70 my $cmp = $self->cmp($ver) + 1; # will be 0 for <, 1 for ==, 2 for >
98 31 100 100     186 return unless $cmp_truth_table{ $op || '>=' }->[$cmp];
99             }
100              
101 8         63 1;
102             }
103              
104             sub TO_JSON {
105 4     4 1 2339 my ($self) = @_;
106 4         45 return [ map { 0 + $_ } @$self ];
  16         53  
107             }
108              
109             1;
110              
111             __END__
112              
113             =head1 NAME
114              
115             Pandoc::Version - version number of pandoc and its libraries
116              
117             =head1 SYNOPSIS
118              
119             $version = Pandoc::Version->new("1.17.2"); # create version
120             $version = bless [1,17,2], 'Pandoc::Version'; # equivalent
121              
122             "$version"; # stringify to "1.17.2"
123             $version > 1.9; # compare
124             $version->[0]; # major
125             $version->[1]; # minor
126              
127             $version->match('1.17'); # true for 1.17, 1.17.x, 1.17.x.y...
128              
129             =head1 DESCRIPTION
130              
131             This module is used to store and compare version numbers of pandoc executable
132             and Haskell libraries compiled into pandoc. A Pandoc::Version object is an
133             array reference of one or more non-negative integer values.
134              
135             In most cases there is no need to create Pandoc::Version objects by hand. Just
136             use the instances returned by methods C<version> and C<libs> of module
137             L<Pandoc> and trust in overloading.
138              
139             =head1 METHODS
140              
141             =head2 string
142              
143             Return a string representation of a version, for instance C<"1.17.0.4">. This
144             method is automatically called by overloading in string context.
145              
146             =head2 number
147              
148             Return a number representation of a version, for instance C<1.017000004>. This
149             method is automatically called by overloading in number context.
150              
151             =head2 cmp( $version )
152              
153             Compare two version numbers. This is method is used automatically by
154             overloading to compare version objects with strings or numbers (operators
155             C<eq>, C<lt>, C<le>, C<ge>, C<==>, C<< < >>, C<< > >>, C<< <= >>, and C<< >=
156             >>).
157              
158             =head2 match( $version )
159              
160             Return whether a version number matches another version number if cut to the
161             same number of parts. For instance C<1.2.3> matches C<1>, C<1.2>, and C<1.2.3>.
162              
163             =head2 fulfills( $version_requirement )
164              
165             Return whether a version number fullfills a version requirement, such as
166             C<!=1.16, <=1.17>. See L<CPAN::Meta::Spec/Version Ranges> for possible values.
167              
168             =head2 TO_JSON
169              
170             Return an array reference of the version number to serialize in JSON format.
171              
172             =head1 SEE ALSO
173              
174             L<version> is a similar module for Perl version numbers.
175              
176             L<SemVer> extends versions to Semantic Versioning as described at L<http://semver.org/>.
177              
178             L<Pandoc::Release> to get information about and download pandoc releases.
179              
180             =cut