Newer
Older
dub_jkp / source / dub / packagesuppliers / filesystem.d
  1. module dub.packagesuppliers.filesystem;
  2.  
  3. import dub.internal.logging;
  4. import dub.internal.vibecompat.inet.path;
  5. import dub.packagesuppliers.packagesupplier;
  6.  
  7. import std.exception : enforce;
  8.  
  9. /**
  10. File system based package supplier.
  11.  
  12. This package supplier searches a certain directory for files with names of
  13. the form "[package name]-[version].zip".
  14. */
  15. class FileSystemPackageSupplier : PackageSupplier {
  16. private {
  17. NativePath m_path;
  18. }
  19.  
  20. this(NativePath root) { m_path = root; }
  21.  
  22. override @property string description() { return "file repository at "~m_path.toNativeString(); }
  23.  
  24. Version[] getVersions(in PackageName name)
  25. {
  26. import std.algorithm.sorting : sort;
  27. import std.file : dirEntries, DirEntry, SpanMode;
  28. import std.conv : to;
  29. import dub.semver : isValidVersion;
  30. Version[] ret;
  31. const zipFileGlob = name.main.toString() ~ "*.zip";
  32. foreach (DirEntry d; dirEntries(m_path.toNativeString(), zipFileGlob, SpanMode.shallow)) {
  33. NativePath p = NativePath(d.name);
  34. auto vers = p.head.name[name.main.toString().length+1..$-4];
  35. if (!isValidVersion(vers)) {
  36. logDebug("Ignoring entry '%s' because it isn't a version of package '%s'", p, name.main);
  37. continue;
  38. }
  39. logDebug("Entry: %s", p);
  40. logDebug("Version: %s", vers);
  41. ret ~= Version(vers);
  42. }
  43. ret.sort();
  44. return ret;
  45. }
  46.  
  47. override ubyte[] fetchPackage(in PackageName name,
  48. in VersionRange dep, bool pre_release)
  49. {
  50. import dub.internal.vibecompat.core.file : readFile, existsFile;
  51. logInfo("Storing package '%s', version requirements: %s", name.main, dep);
  52. auto filename = bestPackageFile(name, dep, pre_release);
  53. enforce(existsFile(filename));
  54. return readFile(filename);
  55. }
  56.  
  57. override Json fetchPackageRecipe(in PackageName name, in VersionRange dep,
  58. bool pre_release)
  59. {
  60. import std.array : split;
  61. import std.path : stripExtension;
  62. import std.algorithm : startsWith, endsWith;
  63. import dub.internal.utils : packageInfoFileFromZip;
  64. import dub.recipe.io : parsePackageRecipe;
  65. import dub.recipe.json : toJson;
  66.  
  67. auto filePath = bestPackageFile(name, dep, pre_release);
  68. string packageFileName;
  69. string packageFileContent = packageInfoFileFromZip(filePath, packageFileName);
  70. auto recipe = parsePackageRecipe(packageFileContent, packageFileName);
  71. Json json = toJson(recipe);
  72. auto basename = filePath.head.name;
  73. enforce(basename.endsWith(".zip"), "Malformed package filename: " ~ filePath.toNativeString);
  74. enforce(basename.startsWith(name.main.toString()),
  75. "Malformed package filename: " ~ filePath.toNativeString);
  76. json["version"] = basename[name.main.toString().length + 1 .. $-4];
  77. return json;
  78. }
  79.  
  80. SearchResult[] searchPackages(string query)
  81. {
  82. // TODO!
  83. return null;
  84. }
  85.  
  86. private NativePath bestPackageFile(in PackageName name, in VersionRange dep,
  87. bool pre_release)
  88. {
  89. import std.algorithm.iteration : filter;
  90. import std.array : array;
  91. import std.format : format;
  92. NativePath toPath(Version ver) {
  93. return m_path ~ "%s-%s.zip".format(name.main, ver);
  94. }
  95. auto versions = getVersions(name).filter!(v => dep.matches(v)).array;
  96. enforce(versions.length > 0, format("No package %s found matching %s", name.main, dep));
  97. foreach_reverse (ver; versions) {
  98. if (pre_release || !ver.isPreRelease)
  99. return toPath(ver);
  100. }
  101. return toPath(versions[$-1]);
  102. }
  103. }