Newer
Older
dub_jkp / source / dub / packagesupplier.d
  1. /**
  2. A package supplier, able to get some packages to the local FS.
  3.  
  4. Copyright: © 2012-2013 Matthias Dondorff
  5. License: Subject to the terms of the MIT license, as written in the included LICENSE.txt file.
  6. Authors: Matthias Dondorff
  7. */
  8. module dub.packagesupplier;
  9.  
  10. import dub.dependency;
  11. import dub.internal.utils;
  12. import dub.internal.vibecompat.core.log;
  13. import dub.internal.vibecompat.core.file;
  14. import dub.internal.vibecompat.data.json;
  15. import dub.internal.vibecompat.inet.url;
  16.  
  17. import std.file;
  18. import std.exception;
  19. import std.zip;
  20. import std.conv;
  21.  
  22. /// Supplies packages, this is done by supplying the latest possible version
  23. /// which is available.
  24. interface PackageSupplier {
  25. /// Returns a hunman readable representation of the supplier
  26. @property string description();
  27.  
  28. /// path: absolute path to store the package (usually in a zip format)
  29. void retrievePackage(Path path, string packageId, Dependency dep, bool pre_release);
  30. /// returns the metadata for the package
  31. Json getPackageDescription(string packageId, Dependency dep, bool pre_release);
  32. }
  33.  
  34. class FileSystemPackageSupplier : PackageSupplier {
  35. private {
  36. Path m_path;
  37. }
  38.  
  39. this(Path root) { m_path = root; }
  40.  
  41. override @property string description() { return "file repository at "~m_path.toNativeString(); }
  42. void retrievePackage(Path path, string packageId, Dependency dep, bool pre_release)
  43. {
  44. enforce(path.absolute);
  45. logInfo("Storing package '"~packageId~"', version requirements: %s", dep);
  46. auto filename = bestPackageFile(packageId, dep, pre_release);
  47. enforce(existsFile(filename));
  48. copyFile(filename, path);
  49. }
  50. Json getPackageDescription(string packageId, Dependency dep, bool pre_release)
  51. {
  52. auto filename = bestPackageFile(packageId, dep, pre_release);
  53. return jsonFromZip(filename, "package.json");
  54. }
  55. private Path bestPackageFile(string packageId, Dependency dep, bool pre_release)
  56. const {
  57. Version bestver = Version.RELEASE;
  58. foreach (DirEntry d; dirEntries(m_path.toNativeString(), packageId~"*", SpanMode.shallow)) {
  59. Path p = Path(d.name);
  60. logDebug("Entry: %s", p);
  61. enforce(to!string(p.head)[$-4..$] == ".zip");
  62. string vers = to!string(p.head)[packageId.length+1..$-4];
  63. logDebug("Version string: "~vers);
  64. Version cur = Version(vers);
  65. if (!dep.matches(cur)) continue;
  66. if (bestver == Version.RELEASE) bestver = cur;
  67. else if (pre_release) {
  68. if (cur > bestver) bestver = cur;
  69. } else if (bestver.isPreRelease) {
  70. if (!cur.isPreRelease || cur > bestver) bestver = cur;
  71. } else if (!cur.isPreRelease && cur > bestver) bestver = cur;
  72. }
  73. auto fileName = m_path ~ (packageId ~ "_" ~ to!string(bestver) ~ ".zip");
  74. if (bestver == Version.RELEASE || !existsFile(fileName))
  75. throw new Exception("No matching package found");
  76. logDiagnostic("Found best matching package: '%s'", fileName);
  77. return fileName;
  78. }
  79. }
  80.  
  81.  
  82. /// Client PackageSupplier using the registry available via registerVpmRegistry
  83. class RegistryPackageSupplier : PackageSupplier {
  84. private {
  85. Url m_registryUrl;
  86. Json[string] m_allMetadata;
  87. }
  88. this(Url registry)
  89. {
  90. m_registryUrl = registry;
  91. }
  92.  
  93. override @property string description() { return "registry at "~m_registryUrl.toString(); }
  94. void retrievePackage(Path path, string packageId, Dependency dep, bool pre_release)
  95. {
  96. Json best = getBestPackage(packageId, dep, pre_release);
  97. auto url = m_registryUrl ~ Path(PackagesPath~"/"~packageId~"/"~best["version"].get!string~".zip");
  98. logDiagnostic("Found download URL: '%s'", url);
  99. download(url, path);
  100. }
  101. Json getPackageDescription(string packageId, Dependency dep, bool pre_release)
  102. {
  103. return getBestPackage(packageId, dep, pre_release);
  104. }
  105. private Json getMetadata(string packageId)
  106. {
  107. if (auto json = packageId in m_allMetadata)
  108. return *json;
  109.  
  110. auto url = m_registryUrl ~ Path(PackagesPath ~ "/" ~ packageId ~ ".json");
  111. logDebug("Downloading metadata for %s", packageId);
  112. logDebug("Getting from %s", url);
  113.  
  114. auto jsonData = cast(string)download(url);
  115. Json json = parseJson(jsonData);
  116. m_allMetadata[packageId] = json;
  117. return json;
  118. }
  119. private Json getBestPackage(string packageId, Dependency dep, bool pre_release)
  120. {
  121. Json md = getMetadata(packageId);
  122. Json best = null;
  123. Version bestver;
  124. foreach (json; md["versions"]) {
  125. auto cur = Version(cast(string)json["version"]);
  126. if (!dep.matches(cur)) continue;
  127. if (best == null) best = json;
  128. else if (pre_release) {
  129. if (cur > bestver) best = json;
  130. } else if (bestver.isPreRelease) {
  131. if (!cur.isPreRelease || cur > bestver) best = json;
  132. } else if (!cur.isPreRelease && cur > bestver) best = json;
  133. bestver = Version(cast(string)best["version"]);
  134. }
  135. enforce(best != null, "No package candidate found for "~packageId~" "~dep.toString());
  136. return best;
  137. }
  138. }
  139.  
  140. private enum PackagesPath = "packages";