Newer
Older
dub_jkp / source / dub / packagesuppliers / maven.d
@WebFreak001 WebFreak001 on 4 Feb 2023 3 KB fix typo(s)
  1. module dub.packagesuppliers.maven;
  2.  
  3. import dub.packagesuppliers.packagesupplier;
  4.  
  5. /**
  6. Maven repository based package supplier.
  7.  
  8. This package supplier connects to a maven repository
  9. to search for available packages.
  10. */
  11. class MavenRegistryPackageSupplier : PackageSupplier {
  12. import dub.internal.utils : retryDownload, HTTPStatusException;
  13. import dub.internal.vibecompat.data.json : serializeToJson;
  14. import dub.internal.vibecompat.inet.url : URL;
  15. import dub.internal.logging;
  16.  
  17. import std.datetime : Clock, Duration, hours, SysTime, UTC;
  18.  
  19. private {
  20. enum httpTimeout = 16;
  21. URL m_mavenUrl;
  22. struct CacheEntry { Json data; SysTime cacheTime; }
  23. CacheEntry[string] m_metadataCache;
  24. Duration m_maxCacheTime;
  25. }
  26.  
  27. this(URL mavenUrl)
  28. {
  29. m_mavenUrl = mavenUrl;
  30. m_maxCacheTime = 24.hours();
  31. }
  32.  
  33. override @property string description() { return "maven repository at "~m_mavenUrl.toString(); }
  34.  
  35. Version[] getVersions(string package_id)
  36. {
  37. import std.algorithm.sorting : sort;
  38. auto md = getMetadata(package_id);
  39. if (md.type == Json.Type.null_)
  40. return null;
  41. Version[] ret;
  42. foreach (json; md["versions"]) {
  43. auto cur = Version(json["version"].get!string);
  44. ret ~= cur;
  45. }
  46. ret.sort();
  47. return ret;
  48. }
  49.  
  50. void fetchPackage(NativePath path, string packageId, Dependency dep, bool pre_release)
  51. {
  52. import std.format : format;
  53. auto md = getMetadata(packageId);
  54. Json best = getBestPackage(md, packageId, dep, pre_release);
  55. if (best.type == Json.Type.null_)
  56. return;
  57. auto vers = best["version"].get!string;
  58. auto url = m_mavenUrl~NativePath("%s/%s/%s-%s.zip".format(packageId, vers, packageId, vers));
  59.  
  60. try {
  61. retryDownload(url, path, 3, httpTimeout);
  62. return;
  63. }
  64. catch(HTTPStatusException e) {
  65. if (e.status == 404) throw e;
  66. else logDebug("Failed to download package %s from %s", packageId, url);
  67. }
  68. catch(Exception e) {
  69. logDebug("Failed to download package %s from %s", packageId, url);
  70. }
  71. throw new Exception("Failed to download package %s from %s".format(packageId, url));
  72. }
  73.  
  74. Json fetchPackageRecipe(string packageId, Dependency dep, bool pre_release)
  75. {
  76. auto md = getMetadata(packageId);
  77. return getBestPackage(md, packageId, dep, pre_release);
  78. }
  79.  
  80. private Json getMetadata(string packageId)
  81. {
  82. import dub.internal.undead.xml;
  83.  
  84. auto now = Clock.currTime(UTC());
  85. if (auto pentry = packageId in m_metadataCache) {
  86. if (pentry.cacheTime + m_maxCacheTime > now)
  87. return pentry.data;
  88. m_metadataCache.remove(packageId);
  89. }
  90.  
  91. auto url = m_mavenUrl~NativePath(packageId~"/maven-metadata.xml");
  92.  
  93. logDebug("Downloading maven metadata for %s", packageId);
  94. string xmlData;
  95.  
  96. try
  97. xmlData = cast(string)retryDownload(url, 3, httpTimeout);
  98. catch(HTTPStatusException e) {
  99. if (e.status == 404) {
  100. logDebug("Maven metadata %s not found at %s (404): %s", packageId, description, e.msg);
  101. return Json(null);
  102. }
  103. else throw e;
  104. }
  105.  
  106. auto json = Json(["name": Json(packageId), "versions": Json.emptyArray]);
  107. auto xml = new DocumentParser(xmlData);
  108.  
  109. xml.onStartTag["versions"] = (ElementParser xml) {
  110. xml.onEndTag["version"] = (in Element e) {
  111. json["versions"] ~= serializeToJson(["name": packageId, "version": e.text]);
  112. };
  113. xml.parse();
  114. };
  115. xml.parse();
  116.  
  117. m_metadataCache[packageId] = CacheEntry(json, now);
  118. return json;
  119. }
  120.  
  121. SearchResult[] searchPackages(string query)
  122. {
  123. // Only exact search is supported
  124. // This enables retrieval of dub packages on dub run
  125. auto md = getMetadata(query);
  126. if (md.type == Json.Type.null_)
  127. return null;
  128. auto json = getBestPackage(md, query, Dependency.any, true);
  129. return [SearchResult(json["name"].opt!string, "", json["version"].opt!string)];
  130. }
  131. }