1 module reggae.dub.interop.fetch;
2 
3 
4 import reggae.from;
5 
6 
7 package void dubFetch(O)(
8     auto ref O output,
9     ref from!"reggae.dub.interop.dublib".Dub dub,
10     in from!"reggae.options".Options options,
11     in string dubSelectionsJson)
12     @trusted
13 {
14     import reggae.io: log;
15     import dub.dub: Dub, FetchOptions;
16     import dub.dependency: Dependency;
17     import dub.project: PlacementLocation;
18     import std.array: replace;
19     import std.json: parseJSON, JSONType;
20     import std.file: readText, getcwd;
21     import std.parallelism: parallel;
22 
23     const(VersionedPackage)[] pkgsToFetch;
24 
25     const json = parseJSON(readText(dubSelectionsJson));
26 
27     foreach(dubPackageName, versionJson; json["versions"].object) {
28 
29         // skip the ones with a defined path
30         if(versionJson.type != JSONType..string) continue;
31 
32         const version_ = versionJson.str.replace("==", "");
33         const pkg = VersionedPackage(dubPackageName, version_);
34 
35         if(needDubFetch(dub, pkg)) pkgsToFetch ~= pkg;
36     }
37 
38     output.log("Creating dub object");
39     auto dubObj = new Dub(getcwd());
40 
41 
42     output.log("Fetching dub packages");
43     foreach(pkg; pkgsToFetch.parallel) {
44         dubObj.fetch(
45             pkg.name,
46             Dependency("==" ~ pkg.version_),
47             PlacementLocation.user,
48             FetchOptions.none,
49         );
50     }
51     output.log("Fetched dub packages");
52 
53     dub.reinit;
54 }
55 
56 
57 private struct VersionedPackage {
58     string name;
59     string version_;
60 }
61 
62 
63 private bool needDubFetch(
64     ref from!"reggae.dub.interop.dublib".Dub dub,
65     in VersionedPackage pkg)
66     @safe
67 {
68     // first check the file system explicitly
69     if(pkgExistsOnFS(pkg)) return false;
70     // next ask dub (this is slower)
71     if(dub.getPackage(pkg.name, pkg.version_)) return false;
72 
73     return true;
74 }
75 
76 
77 // dub fetch can sometimes take >10s (!) despite the package already being
78 // on disk
79 private bool pkgExistsOnFS(in VersionedPackage pkg) @safe {
80     import reggae.path: buildPath, dubPackagesDir;
81     import std.file: exists;
82     import std..string: replace;
83 
84     // Some versions include a `+` and that becomes `_` in the path
85     const version_ = pkg.version_.replace("+", "_");
86 
87     return buildPath(
88         dubPackagesDir,
89         pkg.name ~ "-" ~ version_,
90         pkg.name ~ ".lock"
91     ).exists;
92 }