1 module dub.packagesuppliers.packagesupplier; 2 3 public import dub.dependency : PackageName, Dependency, Version, VersionRange; 4 import dub.dependency : visit; 5 public import dub.internal.vibecompat.core.file : NativePath; 6 public import dub.internal.vibecompat.data.json : Json; 7 8 /** 9 Base interface for remote package suppliers. 10 11 Provides functionality necessary to query package versions, recipes and 12 contents. 13 */ 14 interface PackageSupplier { 15 /// Represents a single package search result. 16 static struct SearchResult { string name, description, version_; } 17 18 /// Returns a human-readable representation of the package supplier. 19 @property string description(); 20 21 /** Retrieves a list of all available versions(/branches) of a package. 22 23 Throws: Throws an exception if the package name is not known, or if 24 an error occurred while retrieving the version list. 25 */ 26 deprecated("Use `getVersions(PackageName)` instead") 27 final Version[] getVersions(string name) 28 { 29 return this.getVersions(PackageName(name)); 30 } 31 32 Version[] getVersions(in PackageName name); 33 34 35 /** Downloads a package and returns its binary content 36 37 Params: 38 name = Name of the package to retrieve 39 dep = Version constraint to match against 40 pre_release = If true, matches the latest pre-release version. 41 Otherwise prefers stable versions. 42 */ 43 ubyte[] fetchPackage(in PackageName name, in VersionRange dep, 44 bool pre_release); 45 46 deprecated("Use `writeFile(path, fetchPackage(PackageName, VersionRange, bool))` instead") 47 final void fetchPackage(in NativePath path, in PackageName name, 48 in VersionRange dep, bool pre_release) 49 { 50 import dub.internal.vibecompat.core.file : writeFile; 51 if (auto res = this.fetchPackage(name, dep, pre_release)) 52 writeFile(path, res); 53 } 54 55 deprecated("Use `fetchPackage(NativePath, PackageName, VersionRange, bool)` instead") 56 final void fetchPackage(NativePath path, string name, Dependency dep, bool pre_release) 57 { 58 return dep.visit!( 59 (const VersionRange rng) { 60 return this.fetchPackage(path, PackageName(name), rng, pre_release); 61 }, (any) { 62 assert(0, "Trying to fetch a package with a non-version dependency: " ~ any.toString()); 63 }, 64 ); 65 } 66 67 /** Retrieves only the recipe of a particular package. 68 69 Params: 70 package_id = Name of the package of which to retrieve the recipe 71 dep = Version constraint to match against 72 pre_release = If true, matches the latest pre-release version. 73 Otherwise prefers stable versions. 74 */ 75 Json fetchPackageRecipe(in PackageName name, in VersionRange dep, bool pre_release); 76 77 deprecated("Use `fetchPackageRecipe(PackageName, VersionRange, bool)` instead") 78 final Json fetchPackageRecipe(string name, Dependency dep, bool pre_release) 79 { 80 return dep.visit!( 81 (const VersionRange rng) { 82 return this.fetchPackageRecipe(PackageName(name), rng, pre_release); 83 }, (any) { 84 return Json.init; 85 }, 86 ); 87 } 88 89 /** Searches for packages matching the given search query term. 90 91 Search queries are currently a simple list of words separated by 92 white space. Results will get ordered from best match to worst. 93 */ 94 SearchResult[] searchPackages(string query); 95 } 96 97 // TODO: Could drop the "best package" behavior and let retrievePackage/ 98 // getPackageDescription take a Version instead of Dependency. But note 99 // this means that two requests to the registry are necessary to retrieve 100 // a package recipe instead of one (first get version list, then the 101 // package recipe) 102 103 package Json getBestPackage(Json metadata, in PackageName name, 104 in VersionRange dep, bool pre_release) 105 { 106 import std.exception : enforce; 107 import std.format : format; 108 109 if (metadata.type == Json.Type.null_) 110 return metadata; 111 Json best = null; 112 Version bestver; 113 foreach (json; metadata["versions"]) { 114 auto cur = Version(json["version"].get!string); 115 if (!dep.matches(cur)) continue; 116 if (best == null) best = json; 117 else if (pre_release) { 118 if (cur > bestver) best = json; 119 } else if (bestver.isPreRelease) { 120 if (!cur.isPreRelease || cur > bestver) best = json; 121 } else if (!cur.isPreRelease && cur > bestver) best = json; 122 bestver = Version(cast(string)best["version"]); 123 } 124 enforce(best != null, 125 "No package candidate found for %s@%s".format(name.main, dep)); 126 return best; 127 }