1 module dub.packagesuppliers.filesystem; 2 3 import dub.packagesuppliers.packagesupplier; 4 5 /** 6 File system based package supplier. 7 8 This package supplier searches a certain directory for files with names of 9 the form "[package name]-[version].zip". 10 */ 11 class FileSystemPackageSupplier : PackageSupplier { 12 import dub.internal.logging; 13 14 version (Have_vibe_core) import dub.internal.vibecompat.inet.path : toNativeString; 15 import std.exception : enforce; 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(string package_id) 25 { 26 import std.algorithm.sorting : sort; 27 import std.file : dirEntries, DirEntry, SpanMode; 28 import std.conv : to; 29 Version[] ret; 30 foreach (DirEntry d; dirEntries(m_path.toNativeString(), package_id~"*", SpanMode.shallow)) { 31 NativePath p = NativePath(d.name); 32 logDebug("Entry: %s", p); 33 enforce(to!string(p.head)[$-4..$] == ".zip"); 34 auto vers = p.head.name[package_id.length+1..$-4]; 35 logDebug("Version: %s", vers); 36 ret ~= Version(vers); 37 } 38 ret.sort(); 39 return ret; 40 } 41 42 void fetchPackage(NativePath path, string packageId, Dependency dep, bool pre_release) 43 { 44 import dub.internal.vibecompat.core.file : copyFile, existsFile; 45 enforce(path.absolute); 46 logInfo("Storing package '%s', version requirements: %s", packageId, dep); 47 auto filename = bestPackageFile(packageId, dep, pre_release); 48 enforce(existsFile(filename)); 49 copyFile(filename, path); 50 } 51 52 Json fetchPackageRecipe(string packageId, Dependency dep, bool pre_release) 53 { 54 import std.array : split; 55 import std.path : stripExtension; 56 import std.algorithm : startsWith, endsWith; 57 import dub.internal.utils : packageInfoFileFromZip; 58 import dub.recipe.io : parsePackageRecipe; 59 import dub.recipe.json : toJson; 60 61 auto filePath = bestPackageFile(packageId, dep, pre_release); 62 string packageFileName; 63 string packageFileContent = packageInfoFileFromZip(filePath, packageFileName); 64 auto recipe = parsePackageRecipe(packageFileContent, packageFileName); 65 Json json = toJson(recipe); 66 auto basename = filePath.head.name; 67 enforce(basename.endsWith(".zip"), "Malformed package filename: " ~ filePath.toNativeString); 68 enforce(basename.startsWith(packageId), "Malformed package filename: " ~ filePath.toNativeString); 69 json["version"] = basename[packageId.length + 1 .. $-4]; 70 return json; 71 } 72 73 SearchResult[] searchPackages(string query) 74 { 75 // TODO! 76 return null; 77 } 78 79 private NativePath bestPackageFile(string packageId, Dependency dep, bool pre_release) 80 { 81 import std.algorithm.iteration : filter; 82 import std.array : array; 83 import std.format : format; 84 NativePath toPath(Version ver) { 85 return m_path ~ (packageId ~ "-" ~ ver.toString() ~ ".zip"); 86 } 87 auto versions = getVersions(packageId).filter!(v => dep.matches(v)).array; 88 enforce(versions.length > 0, format("No package %s found matching %s", packageId, dep)); 89 foreach_reverse (ver; versions) { 90 if (pre_release || !ver.isPreRelease) 91 return toPath(ver); 92 } 93 return toPath(versions[$-1]); 94 } 95 }