Newer
Older
dub_jkp / source / dub / packagesupplier.d
@Sönke Ludwig Sönke Ludwig on 20 Jul 2013 4 KB Refactor code.
/**
	A package supplier, able to get some packages to the local FS.

	Copyright: © 2012 Matthias Dondorff
	License: Subject to the terms of the MIT license, as written in the included LICENSE.txt file.
	Authors: Matthias Dondorff
*/
module dub.packagesupplier;

import dub.dependency;
import dub.internal.vibecompat.core.log;
import dub.internal.vibecompat.core.file;
import dub.internal.vibecompat.data.json;
import dub.internal.vibecompat.inet.url;
import dub.utils;

import std.file;
import std.exception;
import std.zip;
import std.conv;

/// Supplies packages, this is done by supplying the latest possible version
/// which is available.
interface PackageSupplier {
	/// path: absolute path to store the package (usually in a zip format)
	void retrievePackage(Path path, string packageId, Dependency dep);
	
	/// returns the metadata for the package
	Json getPackageDescription(string packageId, Dependency dep);

	/// Returns a hunman readable representation of the supplier
	string toString();
}

class FileSystemPackageSupplier : PackageSupplier {
	private {
		Path m_path;
	}

	this(Path root) { m_path = root; }

	override string toString() { return "file repository at "~m_path.toNativeString(); }
	
	void retrievePackage(Path path, string packageId, Dependency dep)
	{
		enforce(path.absolute);
		logInfo("Storing package '"~packageId~"', version requirements: %s", dep);
		auto filename = bestPackageFile(packageId, dep);
		enforce(existsFile(filename));
		copyFile(filename, path);
	}
	
	Json getPackageDescription(string packageId, Dependency dep)
	{
		auto filename = bestPackageFile(packageId, dep);
		return jsonFromZip(filename, "package.json");
	}
	
	private Path bestPackageFile(string packageId, Dependency dep)
	const {
		Version bestVersion = Version.RELEASE;
		foreach (DirEntry d; dirEntries(m_path.toNativeString(), packageId~"*", SpanMode.shallow)) {
			Path p = Path(d.name);
			logDebug("Entry: %s", p);
			enforce(to!string(p.head)[$-4..$] == ".zip");
			string vers = to!string(p.head)[packageId.length+1..$-4];
			logDebug("Version string: "~vers);
			Version v = Version(vers);
			if (v > bestVersion && dep.matches(v)) {
				bestVersion = v;
			}
		}
		
		auto fileName = m_path ~ (packageId ~ "_" ~ to!string(bestVersion) ~ ".zip");
		
		if (bestVersion == Version.RELEASE || !existsFile(fileName))
			throw new Exception("No matching package found");
		
		logDiagnostic("Found best matching package: '%s'", fileName);
		return fileName;
	}
}


/// Client PackageSupplier using the registry available via registerVpmRegistry
class RegistryPackageSupplier : PackageSupplier {
	private {
		Url m_registryUrl;
		Json[string] m_allMetadata;
	}
	
	this(Url registry)
	{
		m_registryUrl = registry;
	}

	override string toString() { return "registry at "~m_registryUrl.toString(); }
	
	void retrievePackage(Path path, string packageId, Dependency dep)
	{
		Json best = getBestPackage(packageId, dep);
		auto url = m_registryUrl ~ Path(PackagesPath~"/"~packageId~"/"~best["version"].get!string~".zip");
		logDiagnostic("Found download URL: '%s'", url);
		download(url, path);
	}
	
	Json getPackageDescription(string packageId, Dependency dep)
	{
		return getBestPackage(packageId, dep);
	}
	
	private Json getMetadata(string packageId)
	{
		if (auto json = packageId in m_allMetadata)
			return *json;

		auto url = m_registryUrl ~ Path(PackagesPath ~ "/" ~ packageId ~ ".json");
		
		logDebug("Downloading metadata for %s", packageId);
		logDebug("Getting from %s", url);

		auto jsonData = cast(string)download(url);
		Json json = parseJson(jsonData);
		m_allMetadata[packageId] = json;
		return json;
	}
	
	private Json getBestPackage(string packageId, Dependency dep)
	{
		Json md = getMetadata(packageId);
		Json best = null;
		foreach (json; md["versions"]) {
			auto cur = Version(cast(string)json["version"]);
			if (dep.matches(cur) && (best == null || Version(cast(string)best["version"]) < cur))
				best = json;
		}
		enforce(best != null, "No package candidate found for "~packageId~" "~dep.toString());
		return best;
	}
}

private enum PackagesPath = "packages";