/
LocalCache.pm6
113 lines (98 loc) · 4.12 KB
/
LocalCache.pm6
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
use Zef;
use Zef::Distribution::Local;
use Zef::Distribution::DependencySpecification;
use Zef::Utils::FileSystem;
# Intended to:
# 1) Keep track of contents of a directory using a manifest.
# a) full update to recursively search location to discover everything
# b) .store method to be called after something is fetched, allowing
# the single entry to be added to the manifest without having to search
# 2) If a requested identity matches anything found in the manifest already
# then it will return *that* instead of necessarily making net requests
# for other Repository like p6c or CPAN (although such choices are
# made inside Zef::Repository itself)
class Zef::Repository::LocalCache does Repository {
has $.mirrors;
has $.auto-update;
has $.cache is rw;
has @!dists;
method IO(--> IO::Path) { my $dir = $!cache.IO; $dir.mkdir unless $dir.e; $dir }
method available(--> Seq) {
self!gather-dists.map: -> $dist {
Candidate.new(
dist => $dist,
uri => ($dist.source-url || $dist.hash<support><source>),
from => self.id,
as => $dist.identity,
);
}
}
# Rebuild the manifest/index by recursively searching for META files
method update {
LEAVE { self.store(|@!dists) }
self!update;
self!gather-dists;
}
method !update(-->Bool) {
# $.cache/level1/level2/ # dirs containing dist files
my @dirs = $!cache.IO.dir.grep(*.d).map(*.dir.Slip).grep(*.d);
my @dists = grep { .defined }, map { try Zef::Distribution::Local.new($_) }, @dirs;
my $content = join "\n", @dists.map: { join "\0", (.identity, .path) }
so $content ?? self!spurt-package-list($content) !! False;
}
# todo: handle %fields
# note this doesn't apply the $max-results per identity searched, and always returns a 1 dist
# max for a single identity (todo: update to handle $max-results for each @identities)
method search(:$max-results = 5, Bool :$strict, *@identities, *%fields --> Seq) {
return () unless @identities || %fields;
my %specs = @identities.map: { $_ => Zef::Distribution::DependencySpecification.new($_) }
# identities that are cached in the localcache manifest
gather for |self!gather-dists -> $dist {
for @identities.grep({ $dist.contains-spec(%specs{$_}, :$strict) }) -> $wanted-as {
take Candidate.new(
dist => $dist,
uri => $dist.IO.absolute,
as => $wanted-as,
from => self.id,
);
}
}
}
# After the `fetch` phase an app can call `.store` on any Repository that
# provides it, allowing each Repository to do things like keep a simple list of
# identities installed, keep a cache of anything installed (how its used here), etc
method store(*@new --> Bool) {
for @new.unique(:as(*.identity)).map(*.IO.parent.IO).unique -> $from {
try copy-paths( $from, $.cache.IO.child($from.basename) )
}
self!update;
}
method !package-list-path(--> IO::Path) {
my $path = self.IO.child('MANIFEST.zef');
$path.spurt('') unless $path.e;
$path;
}
method !slurp-package-list(--> List) {
return [ ] unless self!package-list-path.e;
do given self!package-list-path.open(:r) {
LEAVE {.close}
.lock: :shared;
.slurp.lines.map({.split("\0")[1]}).cache;
}
}
method !spurt-package-list($content --> Bool) {
do given self!package-list-path.open(:w) {
LEAVE {.close}
.lock;
try .spurt($content);
}
}
# Abstraction to handle automatic updating of package list and/or local index
method !gather-dists(--> List) {
once { self.update } if $.auto-update || !self!package-list-path.e;
return @!dists if +@!dists;
@!dists = gather for self!slurp-package-list.grep(*.IO.e) -> $path {
take($_) with try Zef::Distribution::Local.new($path);
}
}
}