summaryrefslogtreecommitdiffstats
path: root/lib/App/ArchLinux/PackagerTools/CPAN.pm
blob: 175e218ab4eaf7e22386c4b544f4f9ccdde7fb7e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
package App::ArchLinux::PackagerTools::CPAN;
use strictures;

use autodie;
use CPAN::DistnameInfo;
use CPANPLUS::Backend;
use Carp;
use Function::Parameters;
use IO::Zlib;
use Log::Any qw($log);
use Syntax::Keyword::Try;
use version;

=head1 NAME

App::ArchLinux::PackagerTools::CPAN - Methods to interact with CPAN

=head1 SYNOPSIS

use App::ArchLinux::PackagerTools::CPAN;

# synopsis...

=head1 DESCRIPTION

# longer description...

=head1 METHODS

=head2 new

 App::ArchLinux::PackagerTools::CPAN->new();

Returns a new instance.

=cut

method new($class: $deps = {}) {
	$deps->{cpanplus_backend} //= CPANPLUS::Backend->new();
	return $class->new_no_defaults($deps);
}

method new_no_defaults($class: $deps = {}) {
	my $self = {};
	bless $self, $class;
	$self->{deps} = $deps;
	$self->{dist_lc_map} = {};
	$self->{dists} = {};
	$self->_build_dist_index();
	return $self;
}

=head2 Public Methods

=head3 is_newer_version_available

Return 1 if a newer version of the distribution is available, 0 otherwise.

=cut

method is_newer_version_available($dist_name, $version) {
	my $dist = $self->get_dist($dist_name);
	# TODO compare versions rather than not-equal check
	my $dist_version = version->parse($dist->{version});
	if ($dist_version > version->parse($version)) {
		return 1;
	} else {
		return 0;
	}
}

method _build_dist_index() {
	$self->{deps}->{cpanplus_backend}->reload_indices();
	# TODO download pacakges list ourselves instead of using cpanplus for it? We may need cpanplus for other features later so keeping it is probably also fine.

	my $packages_file = "$ENV{HOME}/.cpanplus/02packages.details.txt.gz";
	my $fh = IO::Zlib->new($packages_file, 'r');
	my %seen;
	my $line_number = 0;
	while (<$fh>) {
		# skip meta info (first 9 lines)
		next if $line_number++ < 9;

		#$log->tracef("Parsing line: %s", $_);
		my ($module_name, $module_version, $file) = split /\s+/;

		next if $seen{$file}++ > 0;

		my $dist = CPAN::DistnameInfo->new($file);
		if (!defined $dist->{dist}) {
			$log->debugf("Failed to parse file: %s", $file);
			next;
		}

		my $dist_name = $dist->{dist};
		my $dist_name_lc = lc($dist_name);

		my $dist_version;
		try {
			$dist_version = version->parse($dist->{version});
		} catch {
			$log->debugf("Skipping dist with invalid version: %s", $@);
			$log->debugf("Skipped dist is: %s", $dist);
			next;
		}

		my $index_dist;
		my $index_version = version->parse($self->{dists}->{$dist_name}->{version});
		my $index_name = $self->{dist_lc_map}->{$dist_name_lc};
		$index_dist = $self->{dists}->{$index_name} if defined $index_name;

		if (!defined($index_dist)
			|| (defined($index_dist) && $index_name eq $dist_name && $dist_version > $index_version)
		) {
			$self->{dist_lc_map}->{$dist_name_lc} = $dist_name;
			$self->{dists}->{$dist_name} = {
				dist_name => $dist_name,
				version => $dist->{version},
			};
		} else {
			if ($dist_name ne $index_name && $dist_version < $index_version) {
				confess $log->errorf("Key '%s' already exists in lc_map. This requires a manual exception to map correctly.\nExisting: %s\nCurrent: %s", $dist_name_lc, $index_dist, {$dist->%{qw(dist version)}});
			}
		}
	}
}

method search_dist($name) {
	$name =~ s/::/-/g;
	$log->tracef("Searching for CPAN distribution matching '%s'", $name);

	my $index_name = $self->{dist_lc_map}->{lc($name)};
	if (defined $index_name) {
		return $self->{dists}->{$index_name};
	}

	return;
}

method get_dist($dist_name) {
	$dist_name =~ s/::/-/g;
	$log->tracef("Getting CPAN data for distribution '%s'", $dist_name);

	my $index_name = $self->{dist_lc_map}->{lc($dist_name)};
	if (defined $index_name) {
		return $self->{dists}->{$index_name};
	}

	croak $log->errorf("Failed to find distribution with name %s", $dist_name);
}

1;

__END__