1 module dud.resolve.providier; 2 3 import std.algorithm.iteration : map, filter; 4 import std.algorithm.searching : find; 5 import std.algorithm.sorting : sort; 6 import std.array : array, empty, front; 7 import std.exception : enforce; 8 import std.typecons : Nullable; 9 import std.json; 10 import std.format : format; 11 import dud.pkgdescription : PackageDescription, jsonToPackageDescription; 12 import dud.pkgdescription.duplicate; 13 import dud.semver.semver; 14 import dud.semver.versionrange; 15 16 @safe pure: 17 18 interface PackageProvidier { 19 const(PackageDescription)[] getPackage(string name, 20 const(VersionRange) verRange); 21 22 const(PackageDescriptionVersionRange) getPackage(string name, string ver); 23 } 24 25 struct PackageDescriptionVersionRange { 26 PackageDescription pkg; 27 VersionRange ver; 28 } 29 30 PackageDescriptionVersionRange dup(const(PackageDescriptionVersionRange) i) { 31 return PackageDescriptionVersionRange( 32 dud.pkgdescription.duplicate.dup(i.pkg), 33 i.ver.dup()); 34 } 35 36 struct DumpFileProvidier { 37 // the cache either holds all or non 38 bool isLoaded; 39 const string dumpFileName; 40 PackageDescriptionVersionRange[][string] cache; 41 JSONValue[string] parsedPackages; 42 43 this(string dumpFileName) { 44 this.dumpFileName = dumpFileName; 45 } 46 47 private void makeSureIsLoaded() { 48 import std.file : readText; 49 if(!this.isLoaded) { 50 JSONValue dump = parseJSON(readText(this.dumpFileName)); 51 enforce(dump.type == JSONType.array); 52 foreach(value; dump.arrayNoRef()) { 53 enforce(value.type == JSONType.object); 54 enforce("name" in value && value["name"].type == JSONType..string); 55 string name = value["name"].str(); 56 this.parsedPackages[name] = value; 57 } 58 this.isLoaded = true; 59 } 60 } 61 62 const(PackageDescriptionVersionRange)[] getPackages(string name, 63 string verRange) 64 { 65 Nullable!VersionRange v = parseVersionRange(verRange); 66 enforce(!v.isNull()); 67 return this.getPackages(name, v.get()); 68 } 69 70 const(PackageDescriptionVersionRange)[] getPackages(string name, 71 const(VersionRange) verRange) 72 { 73 import dud.semver.checks : allowsAny; 74 this.makeSureIsLoaded(); 75 auto pkgs = this.ensurePackageIsInCache(name); 76 return (*pkgs) 77 .filter!(pkg => !pkg.ver.isBranch()) 78 .filter!(pkg => allowsAny(verRange, pkg.ver)) 79 .array; 80 } 81 82 PackageDescriptionVersionRange[]* ensurePackageIsInCache(string name) { 83 auto pkgs = name in this.cache; 84 if(pkgs is null) { 85 auto ptr = name in parsedPackages; 86 enforce(ptr !is null, format( 87 "Couldn't find '%s' in dump.json", name)); 88 this.cache[name] = dumpJSONToPackage(*ptr); 89 pkgs = name in this.cache; 90 } 91 return pkgs; 92 } 93 94 const(PackageDescription) getPackage(string name, string ver) { 95 this.makeSureIsLoaded(); 96 auto pkgs = this.ensurePackageIsInCache(name); 97 Nullable!VersionRange v = parseVersionRange(ver); 98 enforce(!v.isNull()); 99 const VersionRange vr = v.get(); 100 101 auto f = (*pkgs).find!((it, s) => it.ver == s)(vr); 102 enforce(!f.empty, format("No version '%s' for package '%s' could" 103 ~ " be found in versions [%s]", name, vr, 104 (*pkgs).map!(it => it.ver))); 105 return f.front.pkg; 106 } 107 } 108 109 private PackageDescriptionVersionRange[] dumpJSONToPackage(JSONValue jv) { 110 enforce(jv.type == JSONType.object, format("Expected object got '%s'", 111 jv.type)); 112 auto vers = "versions" in jv; 113 enforce(vers !is null, "Couldn't find versions array"); 114 enforce((*vers).type == JSONType.array, format("Expected array got '%s'", 115 (*vers).type)); 116 117 return (*vers).arrayNoRef() 118 .map!((it) { 119 auto ptr = "packageDescription" in it; 120 enforce(ptr !is null && (*ptr).type == JSONType.object); 121 PackageDescription pkg = jsonToPackageDescription(*ptr); 122 123 auto ver = "version" in it; 124 enforce(ver !is null && (*ver).type == JSONType..string); 125 Nullable!VersionRange v = parseVersionRange((*ver).str()); 126 enforce(!v.isNull()); 127 const VersionRange vr = v.get(); 128 return PackageDescriptionVersionRange(pkg, vr.dup); 129 }) 130 .array 131 .sort!((a, b) => a.ver > b.ver) 132 .array; 133 }