1 module dub.packagesuppliers.packagesupplier;
2 
3 public import dub.dependency : Dependency, Version;
4 public import dub.internal.vibecompat.core.file : NativePath;
5 public import dub.internal.vibecompat.data.json : Json;
6 
7 /**
8 	Base interface for remote package suppliers.
9 
10 	Provides functionality necessary to query package versions, recipes and
11 	contents.
12 */
13 interface PackageSupplier {
14 	/// Represents a single package search result.
15 	static struct SearchResult { string name, description, version_; }
16 
17 	/// Returns a human-readable representation of the package supplier.
18 	@property string description();
19 
20 	/** Retrieves a list of all available versions(/branches) of a package.
21 
22 		Throws: Throws an exception if the package name is not known, or if
23 			an error occurred while retrieving the version list.
24 	*/
25 	Version[] getVersions(string package_id);
26 
27 	/** Downloads a package and stores it as a ZIP file.
28 
29 		Params:
30 			path = Absolute path of the target ZIP file
31 			package_id = Name of the package to retrieve
32 			dep = Version constraint to match against
33 			pre_release = If true, matches the latest pre-release version.
34 				Otherwise prefers stable versions.
35 	*/
36 	void fetchPackage(NativePath path, string package_id, Dependency dep, bool pre_release);
37 
38 	/** Retrieves only the recipe of a particular package.
39 
40 		Params:
41 			package_id = Name of the package of which to retrieve the recipe
42 			dep = Version constraint to match against
43 			pre_release = If true, matches the latest pre-release version.
44 				Otherwise prefers stable versions.
45 	*/
46 	Json fetchPackageRecipe(string package_id, Dependency dep, bool pre_release);
47 
48 	/** Searches for packages matching the given search query term.
49 
50 		Search queries are currently a simple list of words separated by
51 		white space. Results will get ordered from best match to worst.
52 	*/
53 	SearchResult[] searchPackages(string query);
54 }
55 
56 // TODO: Could drop the "best package" behavior and let retrievePackage/
57 //       getPackageDescription take a Version instead of Dependency. But note
58 //       this means that two requests to the registry are necessary to retrieve
59 //       a package recipe instead of one (first get version list, then the
60 //       package recipe)
61 
62 package Json getBestPackage(Json metadata, string packageId, Dependency dep, bool pre_release)
63 {
64 	import std.exception : enforce;
65 	if (metadata.type == Json.Type.null_)
66 		return metadata;
67 	Json best = null;
68 	Version bestver;
69 	foreach (json; metadata["versions"]) {
70 		auto cur = Version(json["version"].get!string);
71 		if (!dep.matches(cur)) continue;
72 		if (best == null) best = json;
73 		else if (pre_release) {
74 			if (cur > bestver) best = json;
75 		} else if (bestver.isPreRelease) {
76 			if (!cur.isPreRelease || cur > bestver) best = json;
77 		} else if (!cur.isPreRelease && cur > bestver) best = json;
78 		bestver = Version(cast(string)best["version"]);
79 	}
80 	enforce(best != null, "No package candidate found for "~packageId~" "~dep.toString());
81 	return best;
82 }