]> git.vpit.fr Git - perl/modules/CPANPLUS-Dist-Gentoo.git/blob - lib/CPANPLUS/Dist/Gentoo/Atom.pm
Use >= as the default atom range
[perl/modules/CPANPLUS-Dist-Gentoo.git] / lib / CPANPLUS / Dist / Gentoo / Atom.pm
1 package CPANPLUS::Dist::Gentoo::Atom;
2
3 use strict;
4 use warnings;
5
6 =head1 NAME
7
8 CPANPLUS::Dist::Gentoo::Atom - Gentoo atom object.
9
10 =head1 VERSION
11
12 Version 0.10
13
14 =cut
15
16 our $VERSION = '0.10';
17
18 =head1 DESCRIPTION
19
20 This class models Gentoo atoms.
21
22 =cut
23
24 use Carp         ();
25 use Scalar::Util ();
26
27 use overload (
28  '<=>' => \&_spaceship,
29  'cmp' => \&_cmp,
30  '""'  => \&_stringify,
31 );
32
33 use CPANPLUS::Dist::Gentoo::Version;
34
35 my $version_rx = $CPANPLUS::Dist::Gentoo::Version::version_rx;
36
37 =head1 METHODS
38
39 =head2 C<< new category => $category, name => $name [, version => $version, range => $range, ebuild => $ebuild ] >>
40
41 Creates a new L<CPANPLUS::Dist::Gentoo::Atom> object from the supplied C<$category>, C<$name>, C<$version>, C<$range> and C<$ebuild>.
42
43 =cut
44
45 sub new {
46  my $class = shift;
47  $class = ref($class) || $class;
48
49  my %args = @_;
50
51  my ($range, $category, $name, $version);
52  if (defined $args{name}) {
53   ($range, $category, $name, $version) = @args{qw/range category name version/};
54   Carp::confess('Category unspecified') unless defined $category;
55   /[^\w-]/ and Carp::confess('Invalid argument') for $name, $category;
56  } elsif (defined $args{atom}) {
57   my $atom = $args{atom};
58   $atom =~ m{^(<|<=|=|>=|>)?([\w-]+)/([\w-]+)(?:-v?($version_rx))?$}
59                                                or Carp::confess('Invalid atom');
60   ($range, $category, $name, $version) = ($1, $2, $3, $4);
61  } else {
62   Carp::confess('Not enough information for building an atom object');
63  }
64
65  if (defined $version) {
66   unless (Scalar::Util::blessed($version)
67                               and $_->isa('CPANPLUS::Dist::Gentoo::Version')) {
68    $version = CPANPLUS::Dist::Gentoo::Version->new($version);
69   }
70  }
71
72  if (defined $version) {
73   if (defined $range) {
74    Carp::confess("Invalid range $range")
75                                       unless grep $range eq $_, qw|< <= = >= >|;
76   } else {
77    $range = '>=';
78   }
79  } else {
80   Carp::confess('Range atoms require a valid version')
81                                             if defined $range and length $range;
82  }
83
84  bless {
85   category => $category,
86   name     => $name,
87   version  => $version,
88   range    => $range,
89   ebuild   => $args{ebuild},
90  }, $class;
91 }
92
93 =head2 C<new_from_ebuild $ebuild>
94
95 Creates a new L<CPANPLUS::Dist::Gentoo::Atom> object by inferring the category, name and version from the given C<$ebuild>
96
97 =cut
98
99 sub new_from_ebuild {
100  my $class = shift;
101  $class = ref($class) || $class;
102
103  my $ebuild = shift;
104  $ebuild = '' unless defined $ebuild;
105
106  $ebuild =~ m{/([\w-]+)/([\w-]+)/\2-v?($version_rx)\.ebuild$}
107                                              or Carp::confess('Invalid ebuild');
108  my ($category, $name, $version) = ($1, $2, $3);
109
110  return $class->new(
111   category => $category,
112   name     => $name,
113   version  => $version,
114   ebuild   => $ebuild,
115  );
116 }
117
118 BEGIN {
119  eval "sub $_ { \$_[0]->{$_} }" for qw/category name version range ebuild/;
120 }
121
122 =head2 C<category>
123
124 Read-only accessor to the atom category.
125
126 =head2 C<name>
127
128 Read-only accessor to the atom name.
129
130 =head2 C<version>
131
132 Read-only accessor to the L<CPANPLUS::Dist::Gentoo::Version> object associated with the atom.
133
134 =head2 C<range>
135
136 Read-only accessor to the atom range.
137
138 =head2 C<ebuild>
139
140 Read-only accessor to the path of an optional ebuild associated with the atom.
141
142 =head2 C<qualified_name>
143
144 Returns the qualified name for the atom, i.e. C<$category/$name>.
145
146 =cut
147
148 sub qualified_name { join '/', $_[0]->category, $_[0]->name }
149
150 sub _spaceship {
151  my ($a1, $a2, $r) = @_;
152
153  my $v1 = $a1->version;
154
155  my $v2;
156  my $blessed = Scalar::Util::blessed($a2);
157  unless ($blessed and $a2->isa(__PACKAGE__)) {
158   if ($blessed and $a2->isa('CPANPLUS::Dist::Gentoo::Version')) {
159    $v2 = $a2;
160    $a2 = undef;
161   } else {
162    my $maybe_atom = eval { __PACKAGE__->new(atom => $a2) };
163    if (my $err = $@) {
164     $v2 = eval { CPANPLUS::Dist::Gentoo::Version->new($a2) };
165     Carp::confess("Can't compare an atom against something that's not an atom, an atom string ($err), a version or a version string ($@)") if $@;
166     $a2 = undef;
167    } else {
168     $a2 = $maybe_atom;
169    }
170   }
171  }
172
173  if (defined $a2) {
174   $v2 = $a2->version;
175
176   my $p1 = $a1->qualified_name;
177   my $p2 = $a2->qualified_name;
178   Carp::confess("Atoms for different packages $p1 and $p2") unless $p1 eq $p2;
179  }
180
181  ($v1, $v2) = ($v2, $v1) if $r;
182
183  return (defined $v1 or 0) <=> (defined $v2 or 0) unless defined $v1
184                                                      and defined $v2;
185
186  return $v1 <=> $v2;
187 }
188
189 sub _cmp {
190  my ($a1, $a2, $r) = @_;
191
192  my $s1 = $a1->qualified_name;
193  my $v1 = $a1->version;
194  $s1   .= "-$v1" if defined $v1;
195
196  my $s2;
197  if (Scalar::Util::blessed($a2) and $a2->isa(__PACKAGE__)) {
198   $s2    = $a2->qualified_name;
199   my $v2 = $a2->version;
200   $s2   .= "-$v2" if defined $v2;
201  } else {
202   $s2 = $a2;
203  }
204
205  $s1 cmp $s2;
206 }
207
208 sub _stringify {
209  my ($a) = @_;
210
211  my $atom = $a->qualified_name;
212
213  my $version = $a->version;
214  $atom = $a->range . $atom . '-' . $version if defined $version;
215
216  return $atom;
217 }
218
219 my %order = (
220  '<'  => -2,
221  '<=' => -1,
222   '=' =>  0,
223  '>=' =>  1,
224  '>'  =>  2,
225 );
226
227 =head2 C<and @atoms>
228
229 Compute the ranged atom representing the logical AND between C<@atoms> with the same category and name.
230
231 =cut
232
233 sub and {
234  shift unless length ref $_[0];
235
236  my $a1 = shift;
237  return $a1 unless @_;
238
239  my $a2 = shift;
240  $a2 = $a2->and(@_) if @_;
241
242  my $p1 = $a1->qualified_name;
243  my $p2 = $a2->qualified_name;
244  Carp::confess("Atoms for different packages $p1 and $p2") unless $p1 eq $p2;
245
246  my $v1 = $a1->version;
247  return $a2 unless defined $v1;
248  my $r1 = $a1->range; # Defined if $v1 is defined
249
250  my $v2 = $a2->version;
251  return $a1 unless defined $v2;
252  my $r2 = $a2->range; # defined if $v2 is defined
253
254  my $o1 = $order{$r1};
255  my $o2 = $order{$r2};
256
257  Carp::confess("Incompatible ranges $r1$p1 and $r2$p2") if $o1 * $o2 < 0;
258
259  if ($r2 eq '=') {
260   ($a1, $a2) = ($a2, $a1);
261   ($v1, $v2) = ($v2, $v1);
262   ($r1, $r2) = ($r2, $r1);
263   ($o1, $o2) = ($o2, $o1);
264  }
265
266  if ($r1 eq '=') {
267   my $r = $r2 eq '=' ? '==' : $r2;
268   Carp::confess("Version mismatch $v1 $r $v2") unless eval "\$a1 $r \$a2";
269   return $a1;
270  } elsif ($o1 > 0) {
271   return $a1 < $a2 ? $a2 : $a1;
272  } else {
273   return $a1 < $a2 ? $a1 : $a2;
274  }
275 }
276
277 =head2 C<fold @atoms>
278
279 Returns a list built from C<@atoms> but where there's only one atom for a given category and name.
280
281 =cut
282
283 sub fold {
284  shift unless length ref $_[0];
285
286  my %seen;
287  for my $atom (@_) {
288   my $key = $atom->qualified_name;
289
290   my $cur = $seen{$key};
291   $seen{$key} = defined $cur ? $cur->and($atom) : $atom;
292  }
293
294  return values %seen;
295 }
296
297 =pod
298
299 This class provides overloaded methods for numerical comparison, string comparison and strigification.
300
301 =head1 SEE ALSO
302
303 L<CPANPLUS::Dist::Gentoo>, L<CPANPLUS::Dist::Gentoo::Version>.
304
305 =head1 AUTHOR
306
307 Vincent Pit, C<< <perl at profvince.com> >>, L<http://www.profvince.com>.
308
309 You can contact me by mail or on C<irc.perl.org> (vincent).
310
311 =head1 BUGS
312
313 Please report any bugs or feature requests to C<bug-cpanplus-dist-gentoo at rt.cpan.org>, or through the web interface at L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=CPANPLUS-Dist-Gentoo>.
314 I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.
315
316 =head1 SUPPORT
317
318 You can find documentation for this module with the perldoc command.
319
320     perldoc CPANPLUS::Dist::Gentoo
321
322 =head1 COPYRIGHT & LICENSE
323
324 Copyright 2009,2010 Vincent Pit, all rights reserved.
325
326 This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
327
328 =cut
329
330 1; # End of CPANPLUS::Dist::Gentoo::Atom